addons-cm/.github/copilot-instructions.md
snt fee8ec9c45 [DOC] Actualizar documentación y instrucciones con cambios recientes (v18.0.1.3.1)
- [FIX] Actualizar copilot-instructions.md con nuevas secciones:
  * QWeb Template Best Practices (patrón crítico para templates complejos)
  * Eskaera System mejorado con info de lazy loading v18.0.1.3.0+
  * QWeb Template Errors en Common Issues & Solutions
  * Recent Changes Summary (actualizado a 2026-02-18)

- [ADD] Nuevo documento docs/RECENT_CHANGES.md:
  * Timeline completo de cambios (Feb 2-18, 2026)
  * 4 secciones principales de cambios documentados
  * Impacto y acciones requeridas por developers
  * Referencias cruzadas a documentación técnica

- [UPD] README.md principal:
  * website_sale_aplicoop actualizado a v18.0.1.3.1
  * Mención de fixes críticos de v18.0.1.3.1
  * Referencias a FINAL_SOLUTION_SUMMARY.md

- [REF] product_main_seller/README.md:
  * Removidas referencias obsoletas a default_supplier_id
  * Documentación actualizada para usar main_seller_id
  * Simplificada sección de Computation Logic

- [UPD] docs/README.md:
  * Nueva sección "Cambios Recientes"
  * Reorganizado índice de documentación de template fixes
  * Mejorada estructura de secciones de troubleshooting

Cambios Documentados:
 Refactoring product_main_seller (18 Feb) - Removido campo alias
 v18.0.1.3.1 Fixes (16 Feb) - Date calculations y template rendering
 v18.0.1.3.0 Lazy Loading (12 Feb) - Performance improvement 95%
 Template Logic Refactoring (Feb 2-16) - QWeb best practices

+438 líneas de documentación nueva/actualizada
2026-02-18 18:37:43 +01:00

19 KiB

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 position explícito (before, after, inside, replace, attributes)
  • Groups: Referenciar grupos con módulo.xml_id (sales_team.group_sale_manager)
  • Sequence: Usar sequence attribute para ordenar campos en vistas

Translation System

IMPORTANTE: El sistema de traducciones está funcionando correctamente. Seguir estas reglas:

  1. Estructura de carpeta i18n/:

    addon_name/
    ├── i18n/
    │   ├── es.po          # Español (obligatorio)
    │   ├── eu.po          # Euskera (obligatorio)
    │   └── addon_name.pot # Template (generado)
    
  2. 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")
    
  3. Usar _() solo en métodos y código ejecutable:

    def action_confirm(self):
        message = _("Confirmed successfully")
        return {'warning': {'message': message}}
    
  4. 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 exec odoo odoo -d odoo --test-enable --stop-after-init -u addon_name

# 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 .flake8 para reglas específicas
  • pylint: Configurado para Odoo con pylint-odoo plugin

Testing

  • Tests en tests/ de cada addon
  • Naming: test_*.py
  • Herencia: odoo.tests.common.TransactionCase
  • Ejecutar: --test-enable flag

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 interno
  • base.group_system - Administrador
  • sales_team.group_sale_manager - Manager de ventas
  • sales_team.group_sale_salesman - Vendedor
  • purchase.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:

  1. Use product_sale_price_from_pricelist with proper configuration
  2. Set pricelist in Settings > Sales > Automatic Price Configuration
  3. Ensure last_purchase_price_compute_type is NOT set to manual_update
  4. 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:

  1. Move complex logic from template to controller
  2. Use simple attribute access in templates (no conditionals)
  3. Pre-process all display values in Python
  4. 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 TransactionCase for 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:

  1. Title & Summary: Qué hace el addon
  2. Features: Lista de funcionalidades
  3. Dependencies: Addons requeridos
  4. Installation: Comandos de instalación
  5. Configuration: Settings necesarios
  6. Usage: Flujo de trabajo típico
  7. Technical Details: Modelos, campos, métodos
  8. 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 ready
  • dev - Development
  • feature/* - New features
  • fix/* - Bug fixes

Performance Considerations

  • Use @api.depends correctamente para computed fields
  • Prefer search() + write() sobre loops con write()
  • 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:

  1. Administrador crea grupo order con fechas (collection, cutoff, pickup)
  2. Miembros añaden productos a su carrito individual
  3. Sistema valida cutoff date antes de confirmar
  4. Notificaciones automáticas al cambiar estados
  5. 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


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 innecesario default_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