20 KiB
AI Agent Skills & Prompt Guidance
Para máxima productividad y calidad, los agentes AI deben seguir estas pautas y consultar los archivos de skills detallados:
1. Skills por Lenguaje/Stack
- Python/Odoo ORM:
.github/skills/python.md - XML/Odoo Views:
.github/skills/xml.md - HTML/CSS/QWeb:
.github/skills/html_css.md - JavaScript/Odoo Web:
.github/skills/javascript.md
2. Ejecución de tests
Siempre usa:
docker-compose run odoo odoo -d odoo --test-enable --stop-after-init -u addon_name
NOTA: Usa
docker-compose run(noexec) para evitar efectos de caché.
3. Patrones y convenciones clave
- Sigue OCA guidelines y skills.
- No pongas lógica en QWeb, solo acceso a datos preparados en el controller.
- Usa
main_seller_iden vez dedefault_supplier_iden product_main_seller. - Para bulk updates, prefiere
search().write()sobre loops.
4. Errores comunes a evitar
- No uses
_()en definiciones de campos, solo en métodos ejecutables. - No pongas lógica de negocio en JavaScript, solo en Python.
- No uses estilos inline en QWeb salvo casos justificados.
5. Documentación y referencias
- Consulta este archivo para patrones globales.
- Consulta los archivos de skills para detalles por lenguaje.
- Para troubleshooting de templates, revisa
docs/QWEB_BEST_PRACTICES.mdydocs/FINAL_SOLUTION_SUMMARY.md.
GitHub Copilot Instructions - Kidekoop Addons Custom
Project Overview
Este repositorio contiene addons personalizados y modificados de Odoo 18.0. El proyecto combina:
- OCB (Odoo Community Backports): Base de Odoo 18.0 community en
/ocb - Addons OCA: Módulos de la comunidad OCA de los que heredan nuestros addons, modificados para adaptarse a nuestras necesidades
- Addons Custom: Módulos desarrollados por este proyecto
Architecture & Stack
- Odoo Version: 18.0 (OCB)
- Python Version: 3.10+
- Framework: Odoo ORM
- Deployment: Docker Compose
- DB: PostgreSQL
- Languages: Python, XML, JavaScript, QWeb
Code Standards
Python Style
- Seguir OCA guidelines estrictamente
- Usar black para formateo (configurado en
pyproject.toml) - isort para ordenar imports (perfil black)
- flake8 para linting
- pylint con pylint-odoo para verificaciones específicas de Odoo
- Pre-commit hooks activos (ver
.pre-commit-config.yaml)
Odoo Conventions
- Model names: snake_case con punto (
product.price.category) - Class names: PascalCase (
ProductPriceCategory) - File names: snake_case (
product_price_category.py) - XML IDs: módulo.nombre_descriptivo (
product_price_category.view_form) - Manifest: Siempre
__manifest__.py, nunca__openerp__.py
XML/View Standards
- Indent: 4 espacios (no tabs)
- XPath: Usar
positionexplícito (before,after,inside,replace,attributes) - Groups: Referenciar grupos con módulo.xml_id (
sales_team.group_sale_manager) - Sequence: Usar
sequenceattribute para ordenar campos en vistas
Translation System
IMPORTANTE: El sistema de traducciones está funcionando correctamente. Seguir estas reglas:
-
Estructura de carpeta i18n/:
addon_name/ ├── i18n/ │ ├── es.po # Español (obligatorio) │ ├── eu.po # Euskera (obligatorio) │ └── addon_name.pot # Template (generado) -
NO usar
_()en definiciones de campos a nivel de módulo:# ❌ INCORRECTO - causa warnings from odoo import _ name = fields.Char(string=_("Name")) # ✅ CORRECTO - traducción se maneja por .po files name = fields.Char(string="Name") -
Usar
_()solo en métodos y código ejecutable:def action_confirm(self): message = _("Confirmed successfully") return {'warning': {'message': message}} -
Generar/actualizar traducciones:
# Exportar términos a traducir Pedir al usuario generar a través de UI, no sabemos el método correcto para exportar SÓLO las cadenas del addon sin incluir todo el sistema.
Usar sólo polib y apend cadenas en los archivos .po, msmerge corrompe los archivos.
5. **Formato de archivos .po**:
- Encoding: UTF-8
- Content-Type: text/plain; charset=UTF-8
- Language codes: `es`, `eu`, `ca`, `gl`, `pt`, `fr`, `it`
## Project Structure
addons-cm/ ├── .github/ # GitHub configs │ └── copilot-instructions.md # Este archivo ├── ocb/ # Odoo Community Backports (18.0) │ └── addons/ # Addons core de Odoo ├── oca_dependencies.txt # Dependencias OCA ├── requirements.txt # Dependencias Python ├── docker-compose.yml # Configuración Docker ├── Makefile # Comandos útiles │ ├── # === ADDONS OCA NO MODIFICADOS === ├── account_invoice_triple_discount/ # Triple descuento en facturas (OCA) ├── purchase_triple_discount/ # Triple descuento en compras (OCA) ├── product_origin/ # Origen del producto (OCA) ├── product_get_price_helper/ # Helper de precios (OCA) ├── product_main_seller/ # Proveedor principal (OCA) ├── product_price_category/ # Categorías de precio (OCA) │ ├── # === ADDONS CUSTOM === ├── account_invoice_triple_discount_readonly/ # Fix para triple descuento ├── product_price_category_supplier/ # Extensión categorías precio ├── product_sale_price_from_pricelist/ # Auto-cálculo precio venta └── website_sale_aplicoop/ # Sistema eskaera (compras grupo)
## Addon References
**Para arquitectura, detalles de implementación y uso específico de cada addon, consultar su `README.md` individual.**
### Addons OCA No Modificados
- [account_invoice_triple_discount](../account_invoice_triple_discount/README.rst)
- [purchase_triple_discount](../purchase_triple_discount/README.rst)
- [product_origin](../product_origin/README.rst)
- [product_get_price_helper](../product_get_price_helper/README.rst)
- [product_main_seller](../product_main_seller/README.rst)
- [product_price_category](../product_price_category/README.rst)
### Addons Custom Propios
- [account_invoice_triple_discount_readonly](../account_invoice_triple_discount_readonly/README.md) - Fix bug descuentos
- [product_price_category_supplier](../product_price_category_supplier/README.md) - Gestión categorías por proveedor
- [product_sale_price_from_pricelist](../product_sale_price_from_pricelist/README.md) - Auto-precio basado en compra
- [website_sale_aplicoop](../website_sale_aplicoop/README.md) - Sistema eskaera completo
## Development Workflow
### Local Development
```bash
# Iniciar entorno (puertos: 8070=web, 8073=longpolling)
docker-compose up -d
# Actualizar addon
docker-compose exec odoo odoo -d odoo -u addon_name --stop-after-init
# Ver logs
docker-compose logs -f odoo
# Ejecutar tests
docker-compose run odoo odoo -d odoo --test-enable --stop-after-init -u addon_name
# NOTA: Usa `docker-compose run` (no `exec`) para lanzar un contenedor limpio y evitar efectos de caché. Cambia `addon_name` por el módulo a testear.
# Acceder a shell de Odoo
docker-compose exec odoo bash
# Acceder a PostgreSQL
docker-compose exec db psql -U odoo -d odoo
Quality Checks
# Ejecutar todos los checks (usa .pre-commit-config.yaml)
pre-commit run --all-files
# O usar Makefile (ver `make help` para todos los comandos)
make lint # Solo linting (pre-commit)
make format # Formatear código (black + isort)
make check-format # Verificar formateo sin modificar
make flake8 # Ejecutar flake8
make pylint # Ejecutar pylint (todos)
make pylint-required # Solo verificaciones mandatorias
make clean # Limpiar archivos temporales
Tools Configuration
- black: Line length 88, target Python 3.10+ (ver
pyproject.toml) - isort: Profile black, sections: STDLIB > THIRDPARTY > ODOO > ODOO_ADDONS > FIRSTPARTY > LOCALFOLDER
- flake8: Ver
.flake8para reglas específicas - pylint: Configurado para Odoo con
pylint-odooplugin
Testing
- Tests en
tests/de cada addon - Naming:
test_*.py - Herencia:
odoo.tests.common.TransactionCase - Ejecutar:
--test-enableflag
Critical Architecture Patterns
Product Variants Architecture
IMPORTANTE: Los campos de lógica de negocio SIEMPRE van en product.product (variantes), no en product.template:
# ✅ CORRECTO - Lógica en product.product
class ProductProduct(models.Model):
_inherit = 'product.product'
last_purchase_price_updated = fields.Boolean(default=False)
list_price_theoritical = fields.Float(default=0.0)
def _compute_theoritical_price(self):
for product in self:
# Cálculo real por variante
pass
# ✅ CORRECTO - Template solo tiene campos related
class ProductTemplate(models.Model):
_inherit = 'product.template'
last_purchase_price_updated = fields.Boolean(
related='product_variant_ids.last_purchase_price_updated',
readonly=False
)
Por qué: Evita problemas con pricelists y reportes que operan a nivel de variante. Ver product_sale_price_from_pricelist como ejemplo.
QWeb Template Best Practices
CRÍTICO: QWeb tiene limitaciones estrictas con lógica compleja. Siempre mover lógica al controller:
# ❌ MAL - QWeb no puede parsear esto
# <t t-set="price" t-value="price_info.get('price') or product.list_price or 0"/>
# ✅ CORRECTO - Preparar datos en controller
class WebsiteController:
def _prepare_product_display_info(self, product, price_info):
"""Pre-procesar todos los valores para QWeb."""
price = price_info.get(product.id, {}).get('price') or product.list_price or 0.0
return {
'display_price': float(price),
'safe_uom_category': product.uom_id.category_id.name or '',
}
# En template: acceso simple, sin lógica
# <span t-esc="product_display['display_price']"/>
Ver docs/QWEB_BEST_PRACTICES.md para más detalles.
Common Patterns
Extending Models
from odoo import models, fields, api
class ResPartner(models.Model):
_inherit = 'res.partner'
custom_field = fields.Char(string="Custom Field")
@api.depends('field1', 'field2')
def _compute_custom(self):
for record in self:
record.custom_computed = record.field1 + record.field2
Creating Wizards (Transient Models)
class WizardModel(models.TransientModel):
_name = 'wizard.model.name'
_description = "Wizard Description"
def action_confirm(self):
# Business logic
return {'type': 'ir.actions.act_window_close'}
Bulk Updates
# Prefer SQL-level updates for performance
self.env['product.template'].search([
('default_supplier_id', '=', partner_id)
]).write({'price_category_id': category_id})
Notifications
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': _('Success'),
'message': _('Operation completed'),
'type': 'success', # or 'warning', 'danger', 'info'
'sticky': False,
}
}
Logging Pattern
import logging
_logger = logging.getLogger(__name__)
# En métodos de cálculo de precios, usar logging detallado:
_logger.info(
"[PRICE DEBUG] Product %s [%s]: base_price=%.2f, tax_amount=%.2f",
product.default_code or product.name,
product.id,
base_price,
tax_amount,
)
Price Calculation Pattern
# Usar product_get_price_helper para cálculos consistentes
partial_price = product._get_price(qty=1, pricelist=pricelist)
base_price = partial_price.get('value', 0.0) or 0.0
# Siempre validar taxes
if not product.taxes_id:
raise UserError(_("No taxes defined for product %s") % product.name)
Dependencies Management
OCA Dependencies (oca_dependencies.txt)
account-invoicing
product-attribute
purchase-workflow
sale-workflow
Python Dependencies (requirements.txt)
- Versiones específicas para evitar breaking changes
- Incluir herramientas de desarrollo (linters, etc)
Security & Access Rights
Grupos Comunes
base.group_user- Usuario internobase.group_system- Administradorsales_team.group_sale_manager- Manager de ventassales_team.group_sale_salesman- Vendedorpurchase.group_purchase_manager- Manager de compras
Security Files
<!-- security/ir.model.access.csv -->
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_model_user,model.name.user,model_model_name,base.group_user,1,1,1,0
Common Issues & Solutions
Translation Warnings
Problem: WARNING: _() called at import time at module...
Solution: Remove _() from field definitions, use only in methods
XPath Not Found
Problem: Element ... not found in parent view
Solution: Check view inheritance chain, verify XML IDs are correct
Discount Reset Issue
Problem: Multiple discounts being accumulated in discount1
Solution: Use account_invoice_triple_discount_readonly addon
Price Calculation
Problem: Prices not updating from pricelist Solution:
- Use
product_sale_price_from_pricelistwith proper configuration - Set pricelist in Settings > Sales > Automatic Price Configuration
- Ensure
last_purchase_price_compute_typeis NOT set tomanual_update - Verify product has taxes configured (required for price calculation)
Product Variant Issues
Problem: Computed fields not working in pricelists/reports
Solution: Move business logic from product.template to product.product and use related fields in template
Manifest Dependencies
Problem: Module not loading, dependency errors
Solution: Check both __manifest__.py depends AND oca_dependencies.txt for OCA repos
QWeb Template Errors
Problem: TypeError: 'NoneType' object is not callable in templates
Solution:
- Move complex logic from template to controller
- Use simple attribute access in templates (no conditionals)
- Pre-process all display values in Python
- See docs/QWEB_BEST_PRACTICES.md for patterns
Example Pattern:
# Controller: prepare clean data
def _prepare_display_info(self, product):
return {
'price': product.price or 0.0,
'uom': product.uom_id.name or '',
}
# Template: use simple access
<span t-esc="display_info['price']"/>
Testing Guidelines
Unit Tests
- Test business logic in isolation
- Mock external dependencies
- Use
TransactionCasefor DB tests
Integration Tests
- Test workflow end-to-end
- Verify computed fields
- Check access rights
UI Tests (Tours)
odoo.define("module.tour", function (require) {
"use strict";
var tour = require("web_tour.tour");
tour.register(
"tour_name",
{
test: true,
url: "/web",
},
[
// Tour steps
],
);
});
Debugging Tips
Enable Developer Mode
Settings > Activate Developer Mode
Check Logs
docker-compose logs -f odoo | grep ERROR
docker-compose logs -f odoo | grep addon_name
Python Debugger
import pdb; pdb.set_trace() # Set breakpoint
Performance Profiling
--log-level=debug_sql # Show SQL queries
Documentation Standards
README.md Structure
Cada addon debe tener un README.md con:
- Title & Summary: Qué hace el addon
- Features: Lista de funcionalidades
- Dependencies: Addons requeridos
- Installation: Comandos de instalación
- Configuration: Settings necesarios
- Usage: Flujo de trabajo típico
- Technical Details: Modelos, campos, métodos
- Translations: Estado de traducciones (si aplica)
manifest.py Structure
Todos los addons custom deben seguir esta estructura:
# Copyright YEAR - Today AUTHOR
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{ # noqa: B018
"name": "Addon Name",
"version": "18.0.X.Y.Z", # X=major, Y=minor, Z=patch
"category": "category_name",
"summary": "Short description",
"author": "Odoo Community Association (OCA), Your Company",
"maintainers": ["maintainer_github"],
"website": "https://github.com/OCA/repo",
"license": "AGPL-3",
"depends": [
"base",
# Lista ordenada alfabéticamente
],
"data": [
"security/ir.model.access.csv",
"views/actions.xml",
"views/menu.xml",
"views/model_views.xml",
],
}
Code Comments
- Docstrings en clases y métodos públicos
- Comentarios inline para lógica compleja
- TODOs con contexto completo
- Logging detallado en operaciones de precios/descuentos
Version Control
Commit Messages
[TAG] module: Brief description
Longer explanation if needed
Tags: [ADD], [FIX], [IMP], [REF], [REM], [I18N], [DOC]
Branch Strategy
main- Production readydev- Developmentfeature/*- New featuresfix/*- Bug fixes
Performance Considerations
- Use
@api.dependscorrectamente para computed fields - Prefer
search()+write()sobre loops conwrite() - Use
create()con lista de vals para bulk creates - Indexes en campos frecuentemente buscados
- Avoid N+1 queries con
prefetch
Key Business Features
Eskaera System (website_sale_aplicoop)
Sistema completo de compras colaborativas para cooperativas de consumo:
- Group Orders: Pedidos grupales con estados (draft → confirmed → collected → completed)
- Separate Carts: Carrito independiente por miembro y por grupo
- Cutoff Dates: Validación de fechas límite para pedidos
- Pickup Management: Gestión de días de recogida
- Lazy Loading: Carga configurable de productos (v18.0.1.3.0+)
- Multi-language: ES, EU, CA, GL, PT, FR, IT
- Member Tracking: Gestión de miembros activos/inactivos por grupo
Flujo típico:
- Administrador crea grupo order con fechas (collection, cutoff, pickup)
- Miembros añaden productos a su carrito individual
- Sistema valida cutoff date antes de confirmar
- Notificaciones automáticas al cambiar estados
- Tracking de fulfillment por miembro
Configuración Lazy Loading (v18.0.1.3.0+):
Settings > Website > Shop Performance
[✓] Enable Lazy Loading
[20] Products Per Page
Mejoras Recientes:
- v18.0.1.3.1: Fixes críticos de cálculo de fechas
- v18.0.1.3.0: Lazy loading, mejora de rendimiento de 10-20s → 500-800ms
- Refactor de template rendering: Mover lógica QWeb al controller
Ver website_sale_aplicoop/README.md y docs/LAZY_LOADING.md para detalles.
Triple Discount System
Todos los documentos de compra/venta soportan 3 descuentos consecutivos:
# Ejemplo: Precio = 600.00
# Desc. 1 = 50% → 300.00
# Desc. 2 = 50% → 150.00
# Desc. 3 = 50% → 75.00
IMPORTANTE: Usar account_invoice_triple_discount_readonly para evitar bug de acumulación de descuentos.
Automatic Pricing System
product_sale_price_from_pricelist calcula automáticamente precio de venta basado en:
- Último precio de compra (
last_purchase_price_received) - Tipo de cálculo de descuentos (
last_purchase_price_compute_type) - Pricelist configurado en Settings
- Impuestos del producto
Configuración crítica:
# En Settings > Sales > Automatic Price Configuration
product_pricelist_automatic = [ID_pricelist]
# En producto
last_purchase_price_compute_type != "manual_update" # Para auto-cálculo
Resources
- OCA Guidelines: https://github.com/OCA/odoo-community.org/blob/master/website/Contribution/CONTRIBUTING.rst
- Odoo Documentation: https://www.odoo.com/documentation/18.0/
- OCB Repository: https://github.com/OCA/OCB
- OCA Repositories: https://github.com/OCA/
Last Updated: 2026-02-18 Odoo Version: 18.0 Python Version: 3.10+
Recent Changes Summary
- 2026-02-18: Refactor
product_main_seller- Remover alias innecesariodefault_supplier_id - 2026-02-16: v18.0.1.3.1 fixes críticos de cálculo de fechas en Eskaera
- 2026-02-12: v18.0.1.3.0 Lazy loading y fixes de template rendering QWeb
- 2026-02-02: UI improvements y date calculation fixes