700 lines
20 KiB
Markdown
700 lines
20 KiB
Markdown
# 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`.
|
|
|
|
# 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_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
|
|
|
|
```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
|
|
|
|
### 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:
|
|
|
|
```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), 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](../website_sale_aplicoop/README.md) 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-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
|