"custom" said nothing about what the module does. It is really about collecting goods into baskets: the extra detailed-operation columns, the is_collected flag, the Product Summary tab, the per-company validation restrictions and the Basket Assembly operator view all serve that one job. Rename it accordingly. Invert the dependency while at it. A generic warehouse addon was dragging in an entire eCommerce application, and the whole coupling was a single field: stock.move.line.home_delivery, related to picking_id.home_delivery. Everything else was already duck-typed. website_sale_aplicoop now depends on this module and injects its own consumer group columns into these views. This removes duplicated logic rather than relocating it: stock.picking .batch_consumer_group_id re-derived from sale_id a value aplicoop already stored as stock.picking.consumer_group_id, and the duplicate carried no @api.depends, so it never recomputed reliably. The batch transfers list now shows the stored field, which is sortable and groupable. The two aplicoop tests that probed information_schema for the res_company batch_* columns can drop that guard: a real dependency guarantees them. Renaming an addon is not something a migrations/ script can do, since a renamed addon is a brand new module to Odoo and its migration scripts never run. A pre_init_hook does it instead: it fires on install after the Python is imported but before registry.load(), which is the window where remapping ir_model_data makes Odoo reuse the existing tables and columns. is_collected, the summary line table and the company settings all survive untouched. Two details the hook has to get right: - ir_model_constraint.module and ir_model_relation.module are integer FKs with ON DELETE CASCADE, so they must be repointed before the old module row is deleted or the bookkeeping goes with it. - Deleting an ir_model_data row does not cascade to the record it points at. Artifacts handed over to aplicoop only need the xmlid dropped, but artifacts that disappear need the record deleted too, or the field survives as an orphan manual field and the view as a custom view referencing it. Verified against a restored copy of the dev database: 50 xmlids moved, 19 summary lines and 5 collected move lines preserved, no orphans, both test suites green, and the module installs cleanly on a database without website_sale_aplicoop. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
773 lines
25 KiB
Markdown
773 lines
25 KiB
Markdown
# ⚠️ Addons OCA Originales y OCB (Odoo)
|
|
|
|
No modificar el directorio de fuentes de OCB (`ocb/`) ni los siguientes addons OCA originales:
|
|
|
|
- `product_main_seller`
|
|
- `account_invoice_triple_discount`
|
|
- `product_get_price_helper`
|
|
- `product_price_category`
|
|
- `purchase_triple_discount`
|
|
- `base_bank_from_iban`
|
|
- `l10n_es_partner`
|
|
|
|
Estos módulos y el core de Odoo (OCB) solo están para referencia y herencia de nuestros addons custom. Cualquier cambio debe hacerse en los addons propios, nunca en los OCA originales ni en el core OCB.
|
|
|
|
# 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:
|
|
|
|
```bash
|
|
docker-compose run odoo odoo -d odoo --test-enable --stop-after-init -u addon_name
|
|
```
|
|
|
|
> NOTA: Usa `docker-compose run` (no `exec`) 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_id` en vez de `default_supplier_id` en 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.md` y `docs/FINAL_SOLUTION_SUMMARY.md`.
|
|
- **Para documentación de addons**: Sigue los estándares OCA descritos en `docs/OCA_DOCUMENTATION.md`
|
|
- Estructura de directorios readme/ con fragmentos .rst
|
|
- Créditos: Criptomart (autor) + Elika Bilbo (financiador)
|
|
- Logo: CriptoMart en `static/description/icon.png` (ver `docs/LOGO_INSTRUCTIONS.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 `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**:
|
|
|
|
```python
|
|
# ❌ 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**:
|
|
|
|
```python
|
|
def action_confirm(self):
|
|
message = _("Confirmed successfully")
|
|
return {'warning': {'message': message}}
|
|
```
|
|
|
|
4. **Generar/actualizar traducciones**:
|
|
|
|
```bash
|
|
# 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_get_price_helper/ # Helper de precios (OCA)
|
|
├── product_main_seller/ # Proveedor principal (OCA)
|
|
├── product_price_category/ # Categorías de precio (OCA)
|
|
├── base_bank_from_iban/ # Banco desde IBAN (OCA)
|
|
├── l10n_es_partner/ # Adaptación partners/bancos ES (OCA)
|
|
│
|
|
├── # === ADDONS CUSTOM ===
|
|
├── # --- Precios y productos ---
|
|
├── product_sale_price_from_pricelist/ # Auto-cálculo precio venta (+ descuentos supplierinfo)
|
|
├── product_pricelist_total_margin/ # Margen total aditivo (markup / comercial) con límites
|
|
├── product_price_category_supplier/ # Extensión categorías precio por proveedor
|
|
├── product_origin_char/ # Campo origen texto libre por producto
|
|
├── account_invoice_triple_discount_readonly/ # Fix bug acumulación triple descuento
|
|
├── # --- Ventas y web ---
|
|
├── website_sale_aplicoop/ # Sistema eskaera (compras grupo)
|
|
├── website_sale_disable_cart/ # Tienda solo catálogo: desactiva el carrito estándar
|
|
├── website_sale_lazy_loading/ # /shop bajo demanda: scroll infinito o botón "Cargar más"
|
|
├── portal_event_registration/ # Portal: ver registros de eventos + adjuntos
|
|
├── # --- Membresías ---
|
|
├── membership_monthly_invoicing/ # Factura mensual de membresía por socio (cron)
|
|
├── membership_expiry_reminder/ # Email recordatorio de renovación próxima
|
|
├── # --- Logística y contabilidad ---
|
|
├── stock_picking_batch_collect/ # Batch picking: columnas extra + vista operario (Basket Assembly)
|
|
├── account_banking_mandate_batch/ # Crear/validar mandatos SEPA en bloque desde contactos
|
|
├── l10n_es_edi_tbai_reagyp_recibidas/ # Fix TicketBAI REAGYP facturas recibidas (19 → 02)
|
|
├── # --- UI de backend ---
|
|
└── web_list_striped/ # Efecto cebra en todas las listas del backend (sólo CSS)
|
|
|
|
````
|
|
|
|
## Addon References
|
|
|
|
**Para arquitectura, detalles de implementación y uso específico de cada addon, consultar su `README.rst` (documentación oficial) o `README_DEV.md` (detalles técnicos).**
|
|
|
|
### Addons OCA No Modificados
|
|
|
|
- [account_invoice_triple_discount](../account_invoice_triple_discount/README.rst)
|
|
- [purchase_triple_discount](../purchase_triple_discount/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)
|
|
- [base_bank_from_iban](../base_bank_from_iban/README.rst)
|
|
- [l10n_es_partner](../l10n_es_partner/README.rst)
|
|
|
|
### Addons Custom Propios
|
|
|
|
**Precios y productos**
|
|
|
|
- [product_sale_price_from_pricelist](../product_sale_price_from_pricelist/README.rst) - Auto-precio basado en compra (+ descuentos de supplierinfo)
|
|
- [product_pricelist_total_margin](../product_pricelist_total_margin/README.rst) - Margen total aditivo (markup / comercial) con límites globales
|
|
- [product_price_category_supplier](../product_price_category_supplier/README.rst) - Gestión categorías por proveedor
|
|
- [product_origin_char](../product_origin_char/README.rst) - Campo origen de texto libre por producto
|
|
- [account_invoice_triple_discount_readonly](../account_invoice_triple_discount_readonly/README.rst) - Fix bug acumulación descuentos
|
|
|
|
**Ventas y web**
|
|
|
|
- [website_sale_aplicoop](../website_sale_aplicoop/README.rst) - Sistema eskaera completo
|
|
- [website_sale_disable_cart](../website_sale_disable_cart/README.rst) - Tienda solo catálogo: oculta el carrito estándar y redirige `/shop/cart*`
|
|
- [website_sale_lazy_loading](../website_sale_lazy_loading/README.rst) - `/shop` bajo demanda: scroll infinito o botón "Cargar más" en vez del paginador
|
|
- [portal_event_registration](../portal_event_registration/README.rst) - Portal: registros de eventos + adjuntos al chatter
|
|
|
|
**Membresías**
|
|
|
|
- [membership_monthly_invoicing](../membership_monthly_invoicing/README.rst) - Factura mensual de membresía por socio activo (cron)
|
|
- [membership_expiry_reminder](../membership_expiry_reminder/README.rst) - Email automático de recordatorio de renovación
|
|
|
|
**Logística y contabilidad**
|
|
|
|
- [stock_picking_batch_collect](../stock_picking_batch_collect/README.rst) - Columnas extra, resumen por producto y vista operario para batch picking
|
|
- [web_list_striped](../web_list_striped/README.rst) - Efecto cebra en todas las listas del backend (sólo CSS)
|
|
- [account_banking_mandate_batch](../account_banking_mandate_batch/README.rst) - Creación masiva de mandatos SEPA desde contactos
|
|
- [l10n_es_edi_tbai_reagyp_recibidas](../l10n_es_edi_tbai_reagyp_recibidas/README.rst) - Fix TicketBAI REAGYP en facturas recibidas (clave régimen 19 → 02)
|
|
|
|
**Nota**: Todos los addons custom siguen la estructura OCA con readme/ fragmentos. Ver [docs/OCA_DOCUMENTATION.md](../docs/OCA_DOCUMENTATION.md) para detalles.
|
|
|
|
## 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
|
|
|
|
```bash
|
|
# 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`:
|
|
|
|
```python
|
|
# ✅ 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**:
|
|
|
|
```python
|
|
# ❌ 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](../docs/QWEB_BEST_PRACTICES.md) para más detalles.
|
|
|
|
## Common Patterns
|
|
|
|
### Extending Models
|
|
|
|
```python
|
|
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)
|
|
|
|
```python
|
|
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
|
|
|
|
```python
|
|
# Prefer SQL-level updates for performance
|
|
self.env['product.template'].search([
|
|
('default_supplier_id', '=', partner_id)
|
|
]).write({'price_category_id': category_id})
|
|
```
|
|
|
|
### Notifications
|
|
|
|
```python
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': _('Success'),
|
|
'message': _('Operation completed'),
|
|
'type': 'success', # or 'warning', 'danger', 'info'
|
|
'sticky': False,
|
|
}
|
|
}
|
|
```
|
|
|
|
### Logging Pattern
|
|
|
|
```python
|
|
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
|
|
|
|
```python
|
|
# 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
|
|
|
|
```xml
|
|
<!-- 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](../docs/QWEB_BEST_PRACTICES.md) for patterns
|
|
|
|
**Example Pattern**:
|
|
|
|
```python
|
|
# 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)
|
|
|
|
```javascript
|
|
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
|
|
|
|
```bash
|
|
docker-compose logs -f odoo | grep ERROR
|
|
docker-compose logs -f odoo | grep addon_name
|
|
```
|
|
|
|
### Python Debugger
|
|
|
|
```python
|
|
import pdb; pdb.set_trace() # Set breakpoint
|
|
```
|
|
|
|
### Performance Profiling
|
|
|
|
```bash
|
|
--log-level=debug_sql # Show SQL queries
|
|
```
|
|
|
|
## Documentation Standards
|
|
|
|
### OCA README Structure
|
|
|
|
Cada addon custom sigue la **estructura OCA** con fragmentos en `readme/`:
|
|
|
|
- `readme/DESCRIPTION.rst` - Descripción y características
|
|
- `readme/INSTALL.rst` - Instalación y dependencias
|
|
- `readme/CONFIGURE.rst` - Configuración post-instalación
|
|
- `readme/USAGE.rst` - Guía de uso
|
|
- `readme/CONTRIBUTORS.rst` - Contribuidores
|
|
- `readme/CREDITS.rst` - Créditos (Criptomart + Elika Bilbo)
|
|
- `README.rst` - README oficial generado/manual
|
|
- `README_DEV.md` - Notas técnicas para desarrolladores
|
|
|
|
**Ver**: [docs/OCA_DOCUMENTATION.md](../docs/OCA_DOCUMENTATION.md) para estructura completa.
|
|
|
|
### **manifest**.py Structure
|
|
|
|
Todos los addons custom deben seguir esta estructura:
|
|
|
|
```python
|
|
# 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), Criptomart",
|
|
"maintainers": ["Criptomart"],
|
|
"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.rst](../website_sale_aplicoop/README.rst) y [docs/LAZY_LOADING.md](../docs/LAZY_LOADING.md) para detalles.
|
|
|
|
### Triple Discount System
|
|
|
|
Todos los documentos de compra/venta soportan 3 descuentos consecutivos:
|
|
|
|
```python
|
|
# 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**:
|
|
|
|
```python
|
|
# 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-06-04
|
|
**Odoo Version**: 18.0
|
|
**Python Version**: 3.10+
|
|
|
|
## Recent Changes Summary
|
|
|
|
- **2026-06-04**: Nuevos addons custom documentados: `membership_monthly_invoicing`,
|
|
`membership_expiry_reminder`, `account_banking_mandate_batch`, `portal_event_registration`,
|
|
`product_pricelist_total_margin`, `product_origin_char`, `l10n_es_edi_tbai_reagyp_recibidas`.
|
|
`product_origin` (OCA) sustituido por `product_origin_char` (custom).
|
|
- **2026-06-04**: `product_sale_price_from_pricelist` aplica descuentos de proveedor desde `supplierinfo`.
|
|
- **2026-08-27**: `stock_picking_batch_custom` renombrado a `stock_picking_batch_collect`
|
|
(v18.0.2.0.0). Se invierte la dependencia: ya no depende de `website_sale_aplicoop`; es este
|
|
el que depende de él e inyecta las columnas de grupo de consumo. El efecto cebra global se
|
|
extrae al nuevo addon `web_list_striped`. El renombrado en BD lo hace un `pre_init_hook`.
|
|
- **2026-06**: `stock_picking_batch_custom` — vista operario a pantalla completa (Basket Assembly),
|
|
ordenación de operaciones por categoría/producto/partner.
|
|
- **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
|