[DOC] Limpiar documentación temporal y scripts de test obsoletos

This commit is contained in:
snt 2026-02-25 17:47:33 +01:00
parent 464ca48127
commit c367e20fc5
16 changed files with 6 additions and 3787 deletions

View file

@ -1,225 +0,0 @@
# 📚 Documentación del Proyecto - Índice
## 🚀 Lazy Loading v18.0.1.3.0 - Documentación Rápida
¿Buscas información sobre la nueva feature de lazy loading? Empieza aquí:
### ⚡ Solo tengo 5 minutos
👉 **[docs/LAZY_LOADING_QUICK_START.md](docs/LAZY_LOADING_QUICK_START.md)** - TL;DR y setup rápido
### 🔧 Necesito instalar / actualizar
👉 **[docs/UPGRADE_INSTRUCTIONS_v18.0.1.3.0.md](docs/UPGRADE_INSTRUCTIONS_v18.0.1.3.0.md)** - Paso a paso con validación y troubleshooting
### 🎓 Quiero entender la arquitectura
👉 **[docs/LAZY_LOADING.md](docs/LAZY_LOADING.md)** - Detalles técnicos completos
### 📍 No sé dónde empezar
👉 **[docs/LAZY_LOADING_DOCS_INDEX.md](docs/LAZY_LOADING_DOCS_INDEX.md)** - Índice con guía de selección por rol
---
## 📖 Documentación General del Proyecto
### Quick Links
| Categoría | Documento | Propósito |
|-----------|-----------|----------|
| **Start** | [README.md](README.md) | Descripción general del proyecto |
| **Development** | [.github/copilot-instructions.md](.github/copilot-instructions.md) | Guía para desarrollo con IA |
| **All Docs** | [docs/README.md](docs/README.md) | Índice completo de documentación técnica |
---
## 📂 Estructura de Documentación
```
addons-cm/
├── README.md # Descripción general del proyecto
├── docs/ # 📚 Documentación técnica
│ ├── README.md # Índice de todos los docs técnicos
│ │
│ ├── 🚀 LAZY LOADING (v18.0.1.3.0)
│ ├── LAZY_LOADING_QUICK_START.md # ⚡ 5 min - Lo esencial
│ ├── LAZY_LOADING_DOCS_INDEX.md # 📍 Índice con guía por rol
│ ├── LAZY_LOADING.md # 🎓 Detalles técnicos
│ ├── UPGRADE_INSTRUCTIONS_v18.0.1.3.0.md # 🔧 Instalación
│ │
│ ├── 📋 OTROS DOCS
│ ├── LINTERS_README.md # Herramientas de código
│ ├── TRANSLATIONS.md # Sistema de traducciones
│ ├── INSTALACION_COMPLETA.md # Instalación del proyecto
│ ├── RESUMEN_INSTALACION.md # Resumen de instalación
│ ├── CORRECCION_PRECIOS_IVA.md # Precios e impuestos
│ └── TEST_MANUAL.md # Testing manual
├── website_sale_aplicoop/ # 📦 Addon principal
│ ├── README.md # Features y configuración
│ └── CHANGELOG.md # Historial de versiones
└── DOCUMENTATION_UPDATE_SUMMARY.md # 📋 Resumen de cambios (Este proyecto)
```
---
## 🎯 Guía Rápida por Tipo de Usuario
### 👤 Administrador del Sistema
1. **Instalación**: [UPGRADE_INSTRUCTIONS_v18.0.1.3.0.md](docs/UPGRADE_INSTRUCTIONS_v18.0.1.3.0.md)
2. **Configuración**: Settings → Website → Shop Settings
3. **Troubleshooting**: Sección de troubleshooting en UPGRADE_INSTRUCTIONS
4. **Performance**: Sección "Verificación de Rendimiento"
### 👨‍💻 Desarrollador
1. **Arquitectura**: [docs/LAZY_LOADING.md](docs/LAZY_LOADING.md)
2. **Código**: Sección "Code Changes" en LAZY_LOADING.md
3. **Testing**: Sección "Debugging & Testing"
4. **Mejoras**: "Future Improvements" al final
### 🎓 Alguien Nuevo en el Proyecto
1. **Start**: [README.md](README.md)
2. **Features**: [website_sale_aplicoop/README.md](website_sale_aplicoop/README.md)
3. **Lazy Loading**: [docs/LAZY_LOADING_DOCS_INDEX.md](docs/LAZY_LOADING_DOCS_INDEX.md)
4. **Detalles Técnicos**: [.github/copilot-instructions.md](.github/copilot-instructions.md)
### 🚀 Alguien que Solo Quiere Setup Rápido
1. [docs/LAZY_LOADING_QUICK_START.md](docs/LAZY_LOADING_QUICK_START.md) (5 min)
2. Done! ✅
---
## 📊 Resumen de Documentación
### Lazy Loading Feature (v18.0.1.3.0)
**Problema Solucionado**:
- ❌ Antes: Página tarda 10-20 segundos en cargar todos los productos y calcular precios
**Solución**:
- ✅ Después: Página carga en 500-800ms (20x más rápido)
- ✅ Productos se cargan bajo demanda con botón "Load More"
- ✅ Configurable: Activable/desactivable, items por página ajustable
**Documentación Incluida**:
- ✅ Quick Start (5 min)
- ✅ Upgrade Instructions (paso a paso)
- ✅ Technical Documentation (detalles completos)
- ✅ Troubleshooting (4 escenarios comunes)
- ✅ Performance Metrics (verificación)
---
## 🔗 Enlaces Directos
### Lazy Loading
- [⚡ Quick Start](docs/LAZY_LOADING_QUICK_START.md) - Start here (5 min)
- [🔧 Upgrade Instructions](docs/UPGRADE_INSTRUCTIONS_v18.0.1.3.0.md) - Installation & Config
- [🎓 Technical Docs](docs/LAZY_LOADING.md) - Deep dive
- [📍 Documentation Index](docs/LAZY_LOADING_DOCS_INDEX.md) - Navigation guide
### Proyecto General
- [📋 Project README](README.md) - Descripción general
- [📚 Technical Docs](docs/README.md) - Índice de todos los docs
- [🤖 Copilot Guide](.github/copilot-instructions.md) - Desarrollo con IA
- [🧪 Testing](docs/TEST_MANUAL.md) - Manual testing
### Addons Específicos
- [🛍️ website_sale_aplicoop](website_sale_aplicoop/README.md) - Sistema eskaera
- [💰 product_sale_price_from_pricelist](product_sale_price_from_pricelist/README.md) - Auto-pricing
- [📦 product_price_category_supplier](product_price_category_supplier/README.md) - Categorías por proveedor
- [🐛 account_invoice_triple_discount_readonly](account_invoice_triple_discount_readonly/README.md) - Fix de descuentos
---
## 📞 ¿Necesitas Ayuda?
### Selecciona tu situación:
| Situación | Qué leer |
|-----------|----------|
| "¿Qué es lazy loading?" | [LAZY_LOADING_QUICK_START.md](docs/LAZY_LOADING_QUICK_START.md) |
| "¿Cómo instalo?" | [UPGRADE_INSTRUCTIONS_v18.0.1.3.0.md](docs/UPGRADE_INSTRUCTIONS_v18.0.1.3.0.md) |
| "¿Cómo configuro?" | UPGRADE_INSTRUCTIONS → Configuration |
| "¿Cómo verifico que funciona?" | UPGRADE_INSTRUCTIONS → Performance Verification |
| "Algo no funciona" | UPGRADE_INSTRUCTIONS → Troubleshooting |
| "¿Cómo hago rollback?" | UPGRADE_INSTRUCTIONS → Rollback Instructions |
| "Detalles técnicos completos" | [LAZY_LOADING.md](docs/LAZY_LOADING.md) |
| "¿Qué archivos fueron modificados?" | LAZY_LOADING.md → Code Changes |
| "¿Cómo hago testing?" | LAZY_LOADING.md → Debugging & Testing |
---
## ✅ Estado de Documentación
- ✅ **Implementación**: Completada (v18.0.1.3.0)
- ✅ **Quick Start**: Disponible (5 min)
- ✅ **Upgrade Guide**: Disponible (paso a paso)
- ✅ **Technical Docs**: Disponible (600+ líneas)
- ✅ **Troubleshooting**: Disponible (4+ escenarios)
- ✅ **Performance Metrics**: Documentadas (20x mejora)
- ✅ **Backward Compatibility**: Confirmada (desactivable)
---
## 🎓 Aprendizaje Rápido
Para entender rápidamente cómo funciona:
1. **El Problema** (2 min): Lee intro de [LAZY_LOADING_QUICK_START.md](docs/LAZY_LOADING_QUICK_START.md)
2. **La Solución** (2 min): Lee "Installation" en QUICK_START
3. **Verificación** (1 min): Sigue "Verificación Rápida" en QUICK_START
4. **Listo**
Para profundizar → [LAZY_LOADING.md](docs/LAZY_LOADING.md)
---
## 📈 Impacto de Performance
| Métrica | Antes | Después | Mejora |
|---------|-------|---------|--------|
| Carga inicial | 10-20s | 500-800ms | **20x** 🚀 |
| Carga página 2 | — | 200-400ms | — |
| DOM size | 1000+ elementos | 20 elementos | **50x** |
| Rendimiento | Lento | Rápido | ✅ |
---
## 📝 Cambios Principales
### Archivos Modificados (5)
1. `/models/res_config_settings.py` - Configuración
2. `/models/group_order.py` - Paginación backend
3. `/controllers/website_sale.py` - Endpoints HTTP
4. `/views/website_templates.xml` - Templates QWeb
5. `/static/src/js/website_sale.js` - AJAX JavaScript
### Documentación Creada (4)
1. LAZY_LOADING_QUICK_START.md
2. LAZY_LOADING_DOCS_INDEX.md
3. LAZY_LOADING.md
4. UPGRADE_INSTRUCTIONS_v18.0.1.3.0.md
---
## 🚀 Siguientes Pasos
### Inmediato
- [ ] Lee [LAZY_LOADING_QUICK_START.md](docs/LAZY_LOADING_QUICK_START.md)
- [ ] Actualiza a v18.0.1.3.0 si no lo has hecho
### Corto Plazo
- [ ] Configura en Settings (si es necesario)
- [ ] Verifica performance (sección "Verificación" en docs)
### Largo Plazo
- [ ] Monitorea performance en producción
- [ ] Considera mejoras futuras (ver LAZY_LOADING.md)
---
**Última Actualización**: 2026-02-16
**Versión de Documentación**: 1.0
**Odoo Version**: 18.0+
**Lazy Loading Version**: 18.0.1.3.0+
Para comenzar, selecciona la sección que más te interese arriba ☝️

View file

@ -1,273 +0,0 @@
# 📋 Resumen de Documentación Creada - Lazy Loading v18.0.1.3.0
## ✅ Actualización Completa
Se ha completado la actualización de toda la documentación del proyecto para reflejar la nueva feature de lazy loading implementada en `website_sale_aplicoop`.
---
## 📄 Archivos Creados y Actualizados
### 🆕 Nuevos Archivos Creados
#### 1. [docs/LAZY_LOADING_QUICK_START.md](../docs/LAZY_LOADING_QUICK_START.md)
**Tipo**: Guía Rápida (~100 líneas)
**Contenido**:
- ✅ TL;DR - Lo más importante
- ✅ Qué necesitas hacer (actualizar y listo)
- ✅ Métricas de mejora de performance (20x más rápido)
- ✅ Configuración opcional (enable/disable, items per page)
- ✅ Troubleshooting rápido (5 problemas comunes)
- ✅ Verificación rápida (cómo comprobar que funciona)
- ✅ Rollback instructions
- ✅ Enlaces a documentación completa
**Audiencia**: Usuarios que quieren "instalar y olvidar"
---
#### 2. [docs/LAZY_LOADING.md](../docs/LAZY_LOADING.md)
**Tipo**: Documentación Técnica Completa (~600 líneas)
**Contenido**:
- ✅ Descripción detallada del problema (carga 10-20s)
- ✅ Solución implementada (lazy loading + configuración)
- ✅ Arquitectura y diseño del sistema
- ✅ Cambios de código por archivo (5 archivos modificados)
- ✅ Configuración en res_config_settings
- ✅ Endpoints HTTP (eskaera_shop, load_eskaera_page)
- ✅ Métricas de rendimiento (20x más rápido)
- ✅ Guía de testing y debugging
- ✅ Troubleshooting avanzado
- ✅ Roadmap de mejoras futuras
**Audiencia**: Desarrolladores, Administradores Técnicos
---
#### 3. [docs/UPGRADE_INSTRUCTIONS_v18.0.1.3.0.md](../docs/UPGRADE_INSTRUCTIONS_v18.0.1.3.0.md)
**Tipo**: Guía de Actualización e Instalación (~180 líneas)
**Contenido**:
- ✅ Resumen de cambios en v18.0.1.3.0
- ✅ Pasos de actualización paso a paso
- ✅ Configuración de settings (3 opciones)
- ✅ Valores recomendados y explicaciones
- ✅ Checklist de validación post-instalación (4 pasos)
- ✅ Troubleshooting de problemas comunes (4 escenarios):
- "Load More" button not appearing
- Products not loading on button click
- Spinner never disappears
- Page crashes after loading products
- ✅ Método de verificación de rendimiento
- ✅ Instrucciones de rollback
- ✅ Notas importantes sobre comportamiento
**Audiencia**: Administradores de Sistema, DevOps
---
#### 3. [docs/LAZY_LOADING_DOCS_INDEX.md](../docs/LAZY_LOADING_DOCS_INDEX.md)
**Tipo**: Índice Centralizado de Documentación
**Contenido**:
- ✅ Overview de la feature
- ✅ Índice de los 4 documentos relacionados
- ✅ Guía de selección (qué leer según tu rol)
- ✅ Resumen de cambios de código
- ✅ Checklist de implementación
- ✅ Notas importantes y limitaciones
- ✅ Enlaces rápidos a todos los docs
- ✅ Información de impacto y performance
**Audiencia**: Todos (punto de partida recomendado)
---
#### 4. [website_sale_aplicoop/CHANGELOG.md](../website_sale_aplicoop/CHANGELOG.md)
**Tipo**: Registro de Cambios
**Contenido**:
- ✅ v18.0.1.3.0: Lazy loading feature (2 puntos)
- ✅ v18.0.1.2.0: UI improvements (3 puntos)
- ✅ v18.0.1.0.0: Initial release
**Audiencia**: Todos
---
### 🔄 Archivos Actualizados
#### 5. [README.md](../README.md) - Proyecto Principal
**Cambios realizados**:
- ✅ Añadido emoji 🚀 a website_sale_aplicoop en tabla de componentes
- ✅ Añadida nota sobre lazy loading en v18.0.1.3.0 con referencia a docs
- ✅ Añadidos dos enlaces nuevos en sección "Documentos Principales":
- 🚀 [Lazy Loading Documentation](docs/LAZY_LOADING.md)
- 📦 [Upgrade Instructions v18.0.1.3.0](docs/UPGRADE_INSTRUCTIONS_v18.0.1.3.0.md)
---
#### 6. [docs/README.md](../docs/README.md) - Índice de Documentación Técnica
**Cambios realizados**:
- ✅ Añadida nueva sección "Performance & Features (Nuevas)"
- ✅ Tres nuevos enlaces:
- [LAZY_LOADING_DOCS_INDEX.md](LAZY_LOADING_DOCS_INDEX.md)
- [LAZY_LOADING.md](LAZY_LOADING.md)
- [UPGRADE_INSTRUCTIONS_v18.0.1.3.0.md](UPGRADE_INSTRUCTIONS_v18.0.1.3.0.md)
---
#### 7. [website_sale_aplicoop/README.md](../website_sale_aplicoop/README.md) - Addon Específico
**Cambios realizados** (realizados en fase anterior):
- ✅ Añadida feature de lazy loading en lista de features
- ✅ Actualizado changelog con v18.0.1.3.0
- ✅ Descripción detallada de lazy loading en changelog
---
## 🎯 Estructura de Documentación Recomendada
### Para Administradores/Usuarios:
```
1. Lee: docs/LAZY_LOADING_DOCS_INDEX.md (orientación)
2. Luego: docs/UPGRADE_INSTRUCTIONS_v18.0.1.3.0.md (instalación)
3. Si hay dudas: Consulta sección de configuración en website_sale_aplicoop/README.md
4. Si hay problemas: Troubleshooting en UPGRADE_INSTRUCTIONS
```
### Para Desarrolladores:
```
1. Lee: docs/LAZY_LOADING_DOCS_INDEX.md (visión general)
2. Luego: docs/LAZY_LOADING.md (arquitectura técnica)
3. Revisa: Cambios de código en LAZY_LOADING.md (sección "Code Changes")
4. Debugging: Sección "Debugging & Testing" en LAZY_LOADING.md
5. Mejoras: "Future Improvements" al final de LAZY_LOADING.md
```
### Para Troubleshooting:
```
1. Primero: docs/UPGRADE_INSTRUCTIONS_v18.0.1.3.0.md (Troubleshooting section)
2. Si persiste: docs/LAZY_LOADING.md (Debugging & Testing)
3. Para rollback: UPGRADE_INSTRUCTIONS_v18.0.1.3.0.md (Rollback Instructions)
```
---
## 📊 Cobertura de Documentación
| Tema | Covered | Donde |
|------|---------|-------|
| **Problem Statement** | ✅ | LAZY_LOADING.md, UPGRADE_INSTRUCTIONS |
| **Solution Overview** | ✅ | LAZY_LOADING_DOCS_INDEX.md, LAZY_LOADING.md |
| **Architecture** | ✅ | LAZY_LOADING.md |
| **Code Changes** | ✅ | LAZY_LOADING.md (por archivo) |
| **Configuration** | ✅ | UPGRADE_INSTRUCTIONS, website_sale_aplicoop/README.md |
| **Installation** | ✅ | UPGRADE_INSTRUCTIONS |
| **Testing** | ✅ | LAZY_LOADING.md |
| **Troubleshooting** | ✅ | UPGRADE_INSTRUCTIONS, LAZY_LOADING.md |
| **Performance Metrics** | ✅ | Todos los docs |
| **Rollback** | ✅ | UPGRADE_INSTRUCTIONS |
| **Future Improvements** | ✅ | LAZY_LOADING.md |
---
## 🔗 Matriz de Enlaces
Todos los documentos están interconectados para facilitar la navegación:
```
README.md (principal)
├── docs/LAZY_LOADING_DOCS_INDEX.md (índice)
│ ├── docs/LAZY_LOADING.md (técnico)
│ ├── docs/UPGRADE_INSTRUCTIONS_v18.0.1.3.0.md (instalación)
│ ├── website_sale_aplicoop/README.md (addon)
│ └── website_sale_aplicoop/CHANGELOG.md (historial)
├── docs/README.md (índice de docs)
└── website_sale_aplicoop/README.md (addon directo)
```
---
## 📈 Métricas de la Documentación
| Métrica | Valor |
|---------|-------|
| **Archivos nuevos creados** | 4 |
| **Archivos actualizados** | 4 |
| **Líneas de documentación** | ~1,400+ |
| **Secciones documentadas** | 20+ |
| **Ejemplos incluidos** | 15+ |
| **Problemas cubiertos en troubleshooting** | 4 |
| **Mejoras futuras documentadas** | 4 |
---
## ✨ Highlights de la Documentación
### 📌 Punto de Entrada Único
- **[docs/LAZY_LOADING_DOCS_INDEX.md](../docs/LAZY_LOADING_DOCS_INDEX.md)** - Índice con guía de selección según rol
### 📌 Documentación Técnica Completa
- **[docs/LAZY_LOADING.md](../docs/LAZY_LOADING.md)** - 600+ líneas de detalles técnicos, cambios de código, testing, debugging
### 📌 Guía Práctica de Instalación
- **[docs/UPGRADE_INSTRUCTIONS_v18.0.1.3.0.md](../docs/UPGRADE_INSTRUCTIONS_v18.0.1.3.0.md)** - Paso a paso con checklist de validación y troubleshooting
### 📌 Changelog Detallado
- **[website_sale_aplicoop/CHANGELOG.md](../website_sale_aplicoop/CHANGELOG.md)** - Historial completo de versiones
### 📌 README Actualizado
- **[README.md](../README.md)** - Referencia al nuevo feature con enlaces
---
## 🚀 Próximos Pasos
La documentación está completa y lista para:
1. ✅ **Publicación**: Todos los archivos están listos para ser compartidos
2. ✅ **Integración**: Enlaces cruzados correctamente configurados
3. ✅ **Accesibilidad**: Índice centralizado para encontrar información fácilmente
4. ✅ **Mantenibilidad**: Estructura clara para futuras actualizaciones
### Sugerencias Futuras:
- Crear video tutorial (5-10 min) demostrando lazy loading en acción
- Agregar métricas en vivo de performance en Settings UI
- Crear tests automatizados para validar configuración
---
## 📞 Preguntas Frecuentes Documentadas
| Pregunta | Respuesta en |
|----------|-------------|
| ¿Qué es lazy loading? | LAZY_LOADING.md intro |
| ¿Cómo instalo? | UPGRADE_INSTRUCTIONS |
| ¿Cómo configuro? | UPGRADE_INSTRUCTIONS + website_sale_aplicoop/README.md |
| ¿Cómo veo mejora de performance? | UPGRADE_INSTRUCTIONS (Performance Verification) |
| ¿Qué pasa si falla? | UPGRADE_INSTRUCTIONS (Troubleshooting) |
| ¿Puedo deshabilitarlo? | Sí, UPGRADE_INSTRUCTIONS sección Configuration |
| ¿Cómo hago rollback? | UPGRADE_INSTRUCTIONS (Rollback Instructions) |
| ¿Detalles técnicos? | LAZY_LOADING.md |
---
## 🎓 Aprendizaje de Documentación
Esta documentación demuestra:
- ✅ Documentación técnica completa y detallada
- ✅ Guías prácticas paso a paso
- ✅ Índices centralizados para fácil navegación
- ✅ Troubleshooting proactivo
- ✅ Interconexión de documentos
- ✅ Diferentes niveles de profundidad (overview → técnico)
- ✅ Cobertura completa de usuario y desarrollador
---
**Estado**: ✅ COMPLETADO
**Documentación Creada**: 3 archivos nuevos, 4 actualizados
**Líneas Totales**: 1,200+
**Fecha**: 2026-02-16
**Versión Aplicable**: 18.0.1.3.0+
---
¿Necesitas que ajuste algo en la documentación o que cree documentos adicionales?

261
README.md
View file

@ -1,259 +1,10 @@
# Kidekoop - Addons Custom para Odoo 18.0
# Oddo Addons Criptomart
Sistema de gestión de compras colaborativas (grupo de consumo) basado en Odoo 18.0 con módulos personalizados y modificados.
## 🎯 Descripción
Este repositorio contiene los addons personalizados para Kidekoop, un sistema completo de gestión de grupos de consumo que permite:
- **Gestión de compras colaborativas** (eskaera)
- **Sistema de precios multinivel** con categorías y descuentos múltiples
- **Integración con proveedores** y gestión de precios automática
- **Interfaz web moderna** para pedidos grupales
- **Multiidioma** (ES, EU, CA, GL, PT, FR, IT)
## 📦 Componentes del Proyecto
### 1. OCB - Odoo Community Backports
- **Ubicación**: `/ocb`
- **Versión**: 18.0
- **Descripción**: Base de Odoo Community Edition
- **Repositorio**: https://github.com/OCA/OCB
### 2. Addons OCA Modificados
| Addon | Propósito | Repositorio OCA |
|-------|-----------|-----------------|
| [account_invoice_triple_discount](account_invoice_triple_discount/) | Sistema de triple descuento en facturas | account-invoicing |
| [purchase_triple_discount](purchase_triple_discount/) | Sistema de triple descuento en compras | purchase-workflow |
| [product_origin](product_origin/) | Campo de origen del producto | product-attribute |
| [product_get_price_helper](product_get_price_helper/) | Helper para cálculo de precios | product-attribute |
| [product_main_seller](product_main_seller/) | Proveedor principal por producto | purchase-workflow |
| [product_price_category](product_price_category/) | Sistema de categorías de precio | product-attribute |
### 3. Addons Custom Propios
## Addons
| Addon | Propósito | Estado |
|-------|-----------|--------|
| [account_invoice_triple_discount_readonly](account_invoice_triple_discount_readonly/) | Fix para bug de descuentos acumulados | ✅ Estable |
| [product_price_category_supplier](product_price_category_supplier/) | Gestión de categorías por proveedor | ✅ Estable |
| [product_sale_price_from_pricelist](product_sale_price_from_pricelist/) | Auto-cálculo precio venta desde compra | ✅ Estable |
| [website_sale_aplicoop](website_sale_aplicoop/) | Sistema completo de eskaera web | ✅ **v18.0.1.3.1** - Estable |
**✨ Feature v18.0.1.3.0**: `website_sale_aplicoop` incluye **lazy loading configurable** para mejorar el rendimiento de carga de productos (10-20s → 500-800ms).
**🔧 Fixes v18.0.1.3.1**: Correcciones críticas en cálculo de fechas y refactor de template rendering para evitar errores QWeb.
Ver [docs/LAZY_LOADING.md](docs/LAZY_LOADING.md) y [docs/FINAL_SOLUTION_SUMMARY.md](docs/FINAL_SOLUTION_SUMMARY.md) para detalles.
## 🚀 Quick Start
### Requisitos
- Docker & Docker Compose
- Python 3.10+
- PostgreSQL 14+
### Instalación
```bash
# Clonar repositorio
git clone [URL_REPO]
cd addons-cm
# Iniciar entorno
docker-compose up -d
# Verificar logs
docker-compose logs -f odoo
```
### Instalar un addon
```bash
docker-compose exec odoo odoo -d odoo -u addon_name --stop-after-init
```
## 🛠️ Desarrollo
### Estructura de Carpetas
```
addons-cm/
├── .github/ # GitHub Copilot instructions
├── ocb/ # Odoo 18.0 base
├── account_invoice_*/ # Addons de facturación
├── purchase_*/ # Addons de compras
├── product_*/ # Addons de productos
├── website_sale_aplicoop/ # Sistema eskaera
├── docker-compose.yml # Configuración Docker
├── requirements.txt # Dependencias Python
├── oca_dependencies.txt # Dependencias OCA
├── Makefile # Comandos útiles
└── .pre-commit-config.yaml # Hooks de pre-commit
```
### Herramientas de Calidad
```bash
# Ejecutar checks de código
make lint
# Formatear código
make format
# Ejecutar todos los pre-commit hooks
pre-commit run --all-files
# Verificar addon específico
./check_addon.sh addon_name
```
### Tests
```bash
# Ejecutar tests de un addon
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.
# Tests específicos con Python unittest
docker-compose exec odoo python -m pytest addons/addon_name/tests/
```
## 🌍 Sistema de Traducciones
Todos los addons custom incluyen traducciones completas en:
- **Español** (es) - Obligatorio
- **Euskera** (eu) - Obligatorio
- **Catalán** (ca)
- **Gallego** (gl)
- **Portugués** (pt)
- **Francés** (fr)
- **Italiano** (it)
### Actualizar Traducciones
```bash
# Exportar términos traducibles
docker-compose exec odoo odoo \
--addons-path=/mnt/extra-addons \
--i18n-export=/tmp/addon_name.pot \
--modules=addon_name \
--db=odoo
# Actualizar archivos .po
cd addon_name/i18n
msgmerge --update es.po addon_name.pot
msgmerge --update eu.po addon_name.pot
```
**Importante**: No usar `_()` en definiciones de campos a nivel de módulo. Solo usar en métodos.
📖 **[Ver guía completa de traducciones](docs/TRANSLATIONS.md)**
## 📖 Documentación Adicional
Cada addon incluye su propio README.md con:
- Arquitectura y diseño
- Detalles de implementación
- Ejemplos de uso
- Casos de prueba
### Documentos Principales
- [GitHub Copilot Instructions](.github/copilot-instructions.md) - Guía para desarrollo con AI
- [Documentación Técnica](docs/) - Guías de instalación, linters, y troubleshooting
- **[🚀 Lazy Loading Documentation](docs/LAZY_LOADING.md)** - Guía técnica completa sobre la nueva feature de carga lazy
- **[📦 Upgrade Instructions v18.0.1.3.0](docs/UPGRADE_INSTRUCTIONS_v18.0.1.3.0.md)** - Guía de actualización e instalación de lazy loading
- [Makefile](Makefile) - Comandos disponibles
- [requirements.txt](requirements.txt) - Dependencias Python
- [oca_dependencies.txt](oca_dependencies.txt) - Repositorios OCA necesarios
## 🔧 Configuración
### Dependencias OCA
Este proyecto depende de los siguientes repositorios OCA:
- **account-invoicing**: Sistema de facturación extendido
- **product-attribute**: Gestión avanzada de productos
- **purchase-workflow**: Flujos de compra personalizados
- **sale-workflow**: Flujos de venta personalizados
### Configuración de Odoo
Archivo `odoo.conf` incluye:
- Configuración de addons path
- Parámetros de base de datos
- Configuración de workers y límites
## 🐛 Issues Conocidos y Soluciones
### Descuentos Acumulándose en discount1
**Problema**: Al usar triple descuento, todos los descuentos se acumulan en el primer campo.
**Solución**: Instalar `account_invoice_triple_discount_readonly`
### Precio de Venta No Actualiza
**Problema**: El precio de venta no se calcula automáticamente desde el precio de compra.
**Solución**: Configurar `product_sale_price_from_pricelist` correctamente.
### Warnings de Traducción
**Problema**: `WARNING: _() called at import time at module...`
**Solución**: No usar `_()` en definiciones de campos, solo en métodos ejecutables.
## 🤝 Contribuir
### Estándares de Código
- Seguir [OCA Guidelines](https://github.com/OCA/odoo-community.org/blob/master/website/Contribution/CONTRIBUTING.rst)
- Usar **black** para formateo
- Usar **isort** para imports
- Pasar **flake8** y **pylint-odoo**
- Todos los commits deben pasar pre-commit hooks
### Estructura de Commits
```
[TAG] module_name: Brief description
Detailed explanation if needed
Tags: [ADD], [FIX], [IMP], [REF], [REM], [I18N], [DOC]
```
### Testing
Todos los nuevos features deben incluir:
- Tests unitarios
- Tests de integración (si aplica)
- Documentación actualizada
## 📝 License
AGPL-3.0 or later
## 👥 Autores
- **Criptomart** - Development
- **OCA Community** - Base addons
## 🔗 Enlaces
- **Odoo Documentation**: https://www.odoo.com/documentation/18.0/
- **OCA**: https://github.com/OCA
- **OCB**: https://github.com/OCA/OCB
- **OCA Guidelines**: https://github.com/OCA/odoo-community.org
---
**Versión Odoo**: 18.0
**Python**: 3.10+
**Última Actualización**: 2026-02-12
| [account_invoice_triple_discount_readonly](account_invoice_triple_discount_readonly/) | Fix para bug de descuentos acumulados | Alpha |
| [product_price_category_supplier](product_price_category_supplier/) | Gestión de categorías por proveedor | Alpha |
| [product_sale_price_from_pricelist](product_sale_price_from_pricelist/) | Auto-cálculo precio venta desde compra | Alpha |
| [website_sale_aplicoop](website_sale_aplicoop/) | Sistema completo de pedidos para grupos de consumo | Alpha |

View file

@ -1,370 +0,0 @@
# BEFORE & AFTER - Error Fixes
**Document**: Visual comparison of all changes made to fix installation errors
**Date**: 10 de febrero de 2026
**Status**: ✅ All fixed and working
---
## File 1: views/res_partner_views.xml
### Error Description
**ParseError**: "Element '<xpath expr="//notebook/page[@name='purchase']">' cannot be located in parent view"
The XPath path was searching for a page that doesn't exist in Odoo 18.
---
### BEFORE ❌
```xml
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Form view extension: Add field and button in Purchases tab -->
<record id="view_res_partner_form_price_category" model="ir.ui.view">
<field name="name">res.partner.form.price.category</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form" />
<field name="arch" type="xml">
<!-- Add in Purchases tab, supplier section -->
<xpath expr="//notebook/page[@name='purchase']" position="inside"> ← ❌ WRONG!
<group string="Price Category Settings" invisible="not supplier_rank">
<field name="default_price_category_id" />
<button
name="action_update_products_price_category"
type="object"
string="Apply to All Products"
class="btn-primary"
help="Update all products from this supplier with the selected price category"
invisible="not default_price_category_id"
/>
</group>
</xpath>
</field>
</record>
<!-- Tree view extension: Add hidden column for price category -->
<record id="view_res_partner_tree_price_category" model="ir.ui.view">
<field name="name">res.partner.tree.price.category</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_tree" />
<field name="arch" type="xml">
<field name="name" position="after"> ← ❌ WRONG! (field name doesn't exist)
<field name="default_price_category_id" column_invisible="1" />
</field>
</field>
</record>
</odoo>
```
### AFTER ✅
```xml
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Form view extension: Add field and button in Purchases tab -->
<record id="view_res_partner_form_price_category" model="ir.ui.view">
<field name="name">res.partner.form.price.category</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form" />
<field name="arch" type="xml">
<!-- Add in Sales & Purchase tab, inside page -->
<xpath expr="//page[@name='sales_purchases']" position="inside"> ← ✅ CORRECT!
<group string="Price Category Settings" invisible="not supplier_rank">
<field name="default_price_category_id" />
<button
name="action_update_products_price_category"
type="object"
string="Apply to All Products"
class="btn-primary"
help="Update all products from this supplier with the selected price category"
invisible="not default_price_category_id"
/>
</group>
</xpath>
</field>
</record>
<!-- Tree view extension: Add hidden column for price category -->
<record id="view_res_partner_tree_price_category" model="ir.ui.view">
<field name="name">res.partner.tree.price.category</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_tree" />
<field name="arch" type="xml">
<field name="complete_name" position="after"> ← ✅ CORRECT!
<field name="default_price_category_id" column_invisible="1" />
</field>
</field>
</record>
</odoo>
```
### Explanation
| Aspect | Before | After | Reason |
|--------|--------|-------|--------|
| **Form XPath** | `//notebook/page[@name='purchase']` | `//page[@name='sales_purchases']` | Odoo 18 uses 'sales_purchases' for sales/purchase page, not 'purchase' |
| **Tree Field** | `name` | `complete_name` | Tree view uses `<list>` with `complete_name` as first field, not `name` |
---
## File 2: models/res_partner.py
### Error Description
**Warning**: "no translation language detected, skipping translation"
The `_()` function is not available at module import time for field definitions.
---
### BEFORE ❌
```python
# Copyright 2026 Your Company
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import _, api, fields, models
class ResPartner(models.Model):
"""Extend res.partner with default price category for suppliers."""
_inherit = 'res.partner'
default_price_category_id = fields.Many2one(
comodel_name='product.price.category',
string=_('Default Price Category'), ← ❌ WRONG!
help=_('Default price category for products from this supplier'), ← ❌ WRONG!
domain=[],
)
def action_update_products_price_category(self):
"""Open wizard to bulk update products with default price category."""
self.ensure_one()
# Count products where this partner is the default supplier
product_count = self.env['product.template'].search_count([
('default_supplier_id', '=', self.id)
])
# ... rest of method
```
### AFTER ✅
```python
# Copyright 2026 Your Company
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import _, api, fields, models
class ResPartner(models.Model):
"""Extend res.partner with default price category for suppliers."""
_inherit = 'res.partner'
default_price_category_id = fields.Many2one(
comodel_name='product.price.category',
string='Default Price Category', ← ✅ CORRECT!
help='Default price category for products from this supplier', ← ✅ CORRECT!
domain=[],
)
def action_update_products_price_category(self):
"""Open wizard to bulk update products with default price category."""
self.ensure_one()
# Count products where this partner is the default supplier
product_count = self.env['product.template'].search_count([
('default_supplier_id', '=', self.id)
])
# ... rest of method
```
### Explanation
| Point | Before | After | Reason |
|-------|--------|-------|--------|
| **Field string** | `string=_('Default Price Category')` | `string='Default Price Category'` | Odoo extracts field strings automatically; `_()` causes warnings at import time |
| **Field help** | `help=_('Default price category...')` | `help='Default price category...'` | Same reason - automatic extraction, no `_()` needed |
| **Translation Support** | ❌ Causes warning (skipped) | ✅ Automatic extraction works | Strings in field definitions are extracted during module compilation |
---
## File 3: models/wizard_update_product_category.py
### Error Description
**Warnings**: Multiple "no translation language detected" warnings
Same issue as File 2 - `_()` in field definitions at import time.
---
### BEFORE ❌
```python
# Copyright 2026 Your Company
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import _, api, fields, models
class WizardUpdateProductCategory(models.TransientModel):
"""Wizard to confirm and bulk update product price categories."""
_name = 'wizard.update.product.category'
_description = 'Update Product Price Category'
partner_id = fields.Many2one(
comodel_name='res.partner',
string=_('Supplier'), ← ❌ WRONG!
readonly=True,
required=True,
)
partner_name = fields.Char(
string=_('Supplier Name'), ← ❌ WRONG!
readonly=True,
related='partner_id.name',
)
price_category_id = fields.Many2one(
comodel_name='product.price.category',
string=_('Price Category'), ← ❌ WRONG!
readonly=True,
required=True,
)
product_count = fields.Integer(
string=_('Number of Products'), ← ❌ WRONG!
readonly=True,
required=True,
)
def action_confirm(self):
"""Bulk update all products from supplier with default price category."""
# ... method body
```
### AFTER ✅
```python
# Copyright 2026 Your Company
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import _, api, fields, models
class WizardUpdateProductCategory(models.TransientModel):
"""Wizard to confirm and bulk update product price categories."""
_name = 'wizard.update.product.category'
_description = 'Update Product Price Category'
partner_id = fields.Many2one(
comodel_name='res.partner',
string='Supplier', ← ✅ CORRECT!
readonly=True,
required=True,
)
partner_name = fields.Char(
string='Supplier Name', ← ✅ CORRECT!
readonly=True,
related='partner_id.name',
)
price_category_id = fields.Many2one(
comodel_name='product.price.category',
string='Price Category', ← ✅ CORRECT!
readonly=True,
required=True,
)
product_count = fields.Integer(
string='Number of Products', ← ✅ CORRECT!
readonly=True,
required=True,
)
def action_confirm(self):
"""Bulk update all products from supplier with default price category."""
# ... method body
```
### Changes Summary
| Field | Before | After | Count |
|-------|--------|-------|-------|
| `partner_id.string` | `_('Supplier')` | `'Supplier'` | 1 |
| `partner_name.string` | `_('Supplier Name')` | `'Supplier Name'` | 1 |
| `price_category_id.string` | `_('Price Category')` | `'Price Category'` | 1 |
| `product_count.string` | `_('Number of Products')` | `'Number of Products'` | 1 |
| **Total fixes** | **4 `_()` calls** | **Removed** | **4** |
---
## Summary Table
| File | Issue | Before | After | Status |
|------|-------|--------|-------|--------|
| `views/res_partner_views.xml` | Wrong XPath path | `//notebook/page[@name='purchase']` | `//page[@name='sales_purchases']` | ✅ Fixed |
| `views/res_partner_views.xml` | Wrong field name | `<field name="name">` | `<field name="complete_name">` | ✅ Fixed |
| `models/res_partner.py` | `_()` in field def | 2 `_()` calls | Removed | ✅ Fixed |
| `models/wizard_update_product_category.py` | `_()` in field defs | 4 `_()` calls | Removed | ✅ Fixed |
**Total Changes**: 8 modifications across 3 files
**Total Errors Fixed**: 2 categories (XPath + Translation)
**Result**: ✅ **All fixed, addon working**
---
## Installation Results
### Before (with errors):
```
2026-02-10 16:17:56,252 47 INFO odoo odoo.modules.registry: module product_price_category_supplier: creating or updating database tables
2026-02-10 16:17:56,344 47 INFO odoo odoo.modules.loading: loading product_price_category_supplier/security/ir.model.access.csv
2026-02-10 16:17:56,351 47 INFO odoo odoo.modules.loading: loading product_price_category_supplier/views/res_partner_views.xml
2026-02-10 16:17:56,362 47 WARNING odoo odoo.modules.loading: Transient module states were reset
2026-02-10 16:17:56,362 47 ERROR odoo odoo.modules.registry: Failed to load registry
2026-02-10 16:17:56,362 47 CRITICAL odoo odoo.service.server: Failed to initialize database `odoo`.
❌ ParseError: while parsing /mnt/extra-addons/product_price_category_supplier/views/res_partner_views.xml:4
```
### After (fixed):
```
2026-02-10 16:21:04,843 69 INFO odoo odoo.modules.loading: loading product_price_category_supplier/security/ir.model.access.csv
2026-02-10 16:21:04,868 69 INFO odoo odoo.modules.loading: loading product_price_category_supplier/views/res_partner_views.xml
2026-02-10 16:21:04,875 69 INFO odoo odoo.modules.loading: loading product_price_category_supplier/views/wizard_update_product_category.xml
2026-02-10 16:21:04,876 69 INFO odoo odoo.addons.base.models.ir_module: module product_price_category_supplier: loading translation file .../i18n/eu.po
2026-02-10 16:21:04,876 69 INFO odoo odoo.addons.base.models.ir_module: module product_price_category_supplier: loading translation file .../i18n/es.po
2026-02-10 16:21:04,912 69 INFO odoo odoo.modules.loading: Module product_price_category_supplier loaded in 0.68s, 179 queries
✅ Success! No errors.
```
---
## Key Learnings
### 1. Odoo 18 XPath Paths
In Odoo 18.0 partner form:
- **Correct**: `//page[@name='sales_purchases']`
- **Wrong**: `//notebook/page[@name='purchase']`
### 2. Translation in Field Definitions
- **WRONG**: Use `_()` in field string/help definitions ❌
- **CORRECT**: Use plain strings, Odoo extracts automatically ✅
- **Why**: Module loading happens before translation context is ready
### 3. Tree View Field Names (Odoo 18)
- **Correct**: `complete_name` (first field in list view) ✅
- **Wrong**: `name` (doesn't exist in tree/list structure) ❌
---
**Document Status**: ✅ Complete
**Last Updated**: 10 de febrero de 2026
**License**: AGPL-3.0

View file

@ -1,279 +0,0 @@
# ERROR FIX REPORT - product_price_category_supplier
**Date**: 10 de febrero de 2026
**Status**: ✅ FIXED & VERIFIED
**Author**: GitHub Copilot
---
## Summary
El addon tenía 2 categorías de errores que fueron corregidos exitosamente:
1. **Error crítico**: XPath incorrecto en vista XML
2. **Warnings**: Uso de `_()` en definiciones de campos de modelo
**Current Status**: ✅ Addon instalado correctamente en Odoo
---
## Errors Found & Fixed
### 1. ParseError: XPath not found in parent view
**Error Message**:
```
odoo.tools.convert.ParseError: while parsing /mnt/extra-addons/product_price_category_supplier/views/res_partner_views.xml:4
Error while parsing or validating view:
Element '<xpath expr="//notebook/page[@name='purchase']">' cannot be located in parent view
```
**Root Cause**:
- La vista base del partner en Odoo 18.0 no tiene una página con name='purchase'
- La estructura real usa `sales_purchases` para la página que contiene campos de ventas y compras
- El XPath buscaba la estructura incorrecta
**Solution Applied**:
#### File: `views/res_partner_views.xml`
**Change 1 - Form View (Line 11)**:
```diff
- <xpath expr="//notebook/page[@name='purchase']" position="inside">
+ <xpath expr="//page[@name='sales_purchases']" position="inside">
```
**Change 2 - Tree View (Line 27)**:
```diff
- <field name="name" position="after">
+ <field name="complete_name" position="after">
```
**Reason**: El tree view de partner usa `<list>` (no `<tree>`) con `complete_name` como primer campo, no `name`.
---
### 2. Translation Warning - "_() in field definitions at import time"
**Warning Message**:
```
2026-02-10 16:17:56,165 47 WARNING odoo odoo.tools.translate: no translation language detected,
skipping translation <frame at ..., file '...wizard_update_product_category.py', line 21, code WizardUpdateProductCategory>
```
**Root Cause**:
- Uso de `_()` en definiciones de campos de modelo durante la importación del módulo
- En Odoo, cuando los módulos se cargan, el contexto de traducción no está disponible aún
- Los strings en definiciones de campos se extraen automáticamente por Odoo sin necesidad de `_()`
**Solution Applied**:
#### File: `models/res_partner.py` (Lines 13-15)
```diff
default_price_category_id = fields.Many2one(
comodel_name='product.price.category',
- string=_('Default Price Category'),
- help=_('Default price category for products from this supplier'),
+ string='Default Price Category',
+ help='Default price category for products from this supplier',
domain=[],
)
```
#### File: `models/wizard_update_product_category.py` (Lines 15, 21, 27, 34)
```diff
partner_id = fields.Many2one(
comodel_name='res.partner',
- string=_('Supplier'),
+ string='Supplier',
readonly=True,
required=True,
)
partner_name = fields.Char(
- string=_('Supplier Name'),
+ string='Supplier Name',
readonly=True,
related='partner_id.name',
)
price_category_id = fields.Many2one(
comodel_name='product.price.category',
- string=_('Price Category'),
+ string='Price Category',
readonly=True,
required=True,
)
product_count = fields.Integer(
- string=_('Number of Products'),
+ string='Number of Products',
readonly=True,
required=True,
)
```
**Why This Works**:
- Odoo's translation extraction system automatically captures field `string` and `help` values
- No necesita marcador `_()` - se extrae durante la compilación del módulo
- Evita warnings de "no translation language detected"
- Los strings siguen siendo traducibles en archivos .po
---
## Verification Results
### ✅ Installation Success
```
2026-02-10 16:21:04,843 69 INFO odoo odoo.modules.loading: loading product_price_category_supplier/security/ir.model.access.csv
2026-02-10 16:21:04,868 69 INFO odoo odoo.modules.loading: loading product_price_category_supplier/views/res_partner_views.xml
2026-02-10 16:21:04,875 69 INFO odoo odoo.modules.loading: loading product_price_category_supplier/views/wizard_update_product_category.xml
2026-02-10 16:21:04,876 69 INFO odoo odoo.addons.base.models.ir_module: module product_price_category_supplier: loading translation file .../i18n/eu.po
2026-02-10 16:21:04,876 69 INFO odoo odoo.addons.base.models.ir_module: module product_price_category_supplier: loading translation file .../i18n/es.po
2026-02-10 16:21:04,912 69 INFO odoo odoo.modules.loading: Module product_price_category_supplier loaded in 0.68s, 179 queries
✅ No errors
✅ No critical warnings
✅ Translations loaded successfully (es, eu)
```
### Database Changes Applied
- ✅ Table `wizard_update_product_category` created
- ✅ Field `default_price_category_id` added to `res_partner`
- ✅ View records created in `ir.ui.view`
- ✅ Security ACL created in `ir.model.access`
---
## Files Modified
| File | Changes | Status |
|------|---------|--------|
| `views/res_partner_views.xml` | XPath corrected (2 changes) | ✅ Fixed |
| `models/res_partner.py` | `_()` removed from field definition (1 change) | ✅ Fixed |
| `models/wizard_update_product_category.py` | `_()` removed from 4 field definitions | ✅ Fixed |
---
## What Was NOT Changed
❌ Translation strings WERE NOT removed from:
- Python code logic (methods still use `_()` for user-facing messages)
- View templates (unchanged - Odoo extracts them automatically)
- XML button labels and help text (extracted by Odoo)
The fix only removed `_()` from **field definition string/help parameters**, where translation happens at extraction time, not at runtime.
---
## Testing Recommendations
### 1. Verify Field Appears in UI
```
1. Go to Contacts (Contactos)
2. Select a supplier (supplier_rank > 0)
3. Open "Sales & Purchase" tab
4. Should see "Price Category Settings" group with:
- "Default Price Category" field
- "Apply to All Products" button
```
### 2. Test Bulk Update Functionality
```
1. Create a price category (e.g., "Premium")
2. Select supplier with products
3. Set supplier's "Default Price Category" to "Premium"
4. Click "Apply to All Products"
5. Confirm in wizard modal
6. Verify products updated in product list
```
### 3. Test Translations
```
1. User preferences → Change Language to Spanish (es)
2. Verify labels display in Spanish:
- "Categoría de Precio Predeterminada"
- "Aplicar a Todos los Productos"
3. Repeat for Euskera (eu)
```
### 4. Verify Permissions
```
1. Test with sales_team.group_sale_manager → Should have access ✅
2. Test with basic user → Should not see wizard access ✅
```
---
## Architecture Notes
### Odoo 18.0 Partner Form Structure
The corrected XPath path now properly targets:
```xml
<record id="view_partner_form" model="ir.ui.view">
<field name="arch" type="xml">
<form>
<notebook>
<page name="contact_addresses">...</page>
<page name="sales_purchases"> ← We insert here
...existing fields...
</page>
<page name="internal_notes">...</page>
</notebook>
</form>
</field>
</record>
```
### Tree View Structure (List in Odoo 18)
```xml
<record id="view_partner_tree" model="ir.ui.view">
<field name="arch" type="xml">
<list>
<field name="complete_name"> ← First field (was: name)
<field name="default_price_category_id" column_invisible="1"/> ← We add after
...
</list>
</field>
</record>
```
---
## Installation Command
```bash
cd /home/snt/Documentos/lab/odoo/kidekoop/odoo-addons
docker-compose exec -T odoo odoo -d odoo -i product_price_category_supplier --stop-after-init
```
**Result**: ✅ Successfully loaded in 0.68s with no errors
---
## Summary of Changes
**Total Files Modified**: 3
**Total Changes**: 8
**Status**: ✅ All Fixed & Tested
The addon is now **ready for production use** with proper:
- ✅ View inheritance (correct XPath paths)
- ✅ Translation support (no runtime warnings)
- ✅ Security configuration (group-based access)
- ✅ Database schema (tables and fields created)
- ✅ Internationalization (Spanish + Euskera)
---
**Maintained by**: Criptomart | **License**: AGPL-3.0
**Last Updated**: 10 de febrero de 2026

View file

@ -1,230 +0,0 @@
✅ IMPLEMENTACIÓN COMPLETA: product_price_category_supplier
================================================================
📦 ADDON CREADO
- Ubicación: /home/snt/Documentos/lab/odoo/kidekoop/odoo-addons/product_price_category_supplier/
- Licencia: AGPL-3.0
- Versión: 18.0.1.0.0
- Dependencias: product_price_category, product_pricelists_margins_custom, sales_team
🎯 FUNCIONALIDADES
✓ Campo en res.partner: "Default Price Category" (en pestaña Compras)
✓ Botón "Apply to All Products" que abre wizard modal
✓ Wizard con confirmación antes de actualizar masivamente
✓ Bulk update de todos los productos del proveedor
✓ Campo oculto en tree view de partner (column_invisible=1, configurable desde menú)
✓ Control de permisos: solo sales_team.group_sale_manager
✓ Traducciones: Español (es) y Euskera (eu)
📁 ESTRUCTURA DE ARCHIVOS
product_price_category_supplier/
├── __init__.py (Root init)
├── __manifest__.py (Metadata + dependencies)
├── models/
│ ├── __init__.py
│ ├── res_partner.py (Campo + método para abrir wizard)
│ └── wizard_update_product_category.py (Transient wizard con bulk update)
├── views/
│ ├── res_partner_views.xml (Form + Tree)
│ └── wizard_update_product_category.xml (Modal wizard)
├── security/
│ └── ir.model.access.csv (Permisos: solo sales_manager)
├── tests/
│ ├── __init__.py
│ └── test_product_price_category_supplier.py (5 tests unitarios)
├── i18n/
│ ├── product_price_category_supplier.pot (Template)
│ ├── es.po (Traducciones español)
│ └── eu.po (Traducciones euskera)
├── README.md (Documentación completa)
├── VALIDATION.md (Validación de componentes)
├── install_addon.sh (Helper script)
└── IMPLEMENTACION_RESUMEN.txt (Este archivo)
🔧 MODELOS
res.partner (Extensión)
- default_price_category_id (Many2one → product.price.category)
- action_update_products_price_category() → Abre wizard modal
wizard.update.product.category (Transient Model)
- partner_id (Many2one → res.partner, readonly)
- partner_name (Char, readonly, related)
- price_category_id (Many2one → product.price.category, readonly)
- product_count (Integer, readonly)
- action_confirm() → Realiza bulk update y retorna notificación
🎨 VISTAS
res.partner Form View
- XPath en página "Purchases"
- Grupo "Price Category Settings"
- Campo: default_price_category_id
- Botón: "Apply to All Products" (invisible si no hay categoría)
- Grupo invisible si no es proveedor (supplier_rank)
res.partner Tree View
- Campo default_price_category_id con column_invisible="1"
- Configurable manualmente desde menú de columnas
wizard.update.product.category Form View
- Alert box de confirmación
- Información: Proveedor, Categoría, Cantidad de productos
- Botones: Confirm (btn-primary), Cancel
🔐 SEGURIDAD
- Modelo: wizard.update.product.category
- Grupo: sales_team.group_sale_manager (Gestores de Ventas)
- Permisos: read=1, write=1, create=1, unlink=1
🌍 TRADUCCIONES
Spanish (es):
✓ "Categoría de Precio por Defecto"
✓ "Aplicar a Todos los Productos"
✓ "Actualizar categoría de precio de producto"
✓ + 20 más strings traducidos
Basque (eu):
✓ "Prezioak Kategoria Lehenetsia"
✓ "Produktu Guztiei Aplikatu"
✓ "Produktuaren Prezioak Kategoria Eguneratu"
✓ + 20 más strings traducidos
✅ FLUJO DE USUARIO
1. Abrir formulario de Proveedor (res.partner)
2. Ir a pestaña "Compras" (Purchases)
3. En sección "Configuración de Categoría de Precio"
4. Seleccionar "Categoría de Precio por Defecto"
5. Hacer clic en botón "Aplicar a Todos los Productos"
6. Se abre modal wizard mostrando:
- "Estás a punto de actualizar X producto(s) de [PROVEEDOR]"
- "con categoría de precio [CATEGORÍA]"
- "Esta acción no se puede deshacer"
7. Clic "Confirmar"
8. Se ejecuta bulk update
9. Notificación de éxito: "X productos actualizados con categoría Y"
❌ CASOS ESPECIALES
- Si proveedor no tiene productos: warning "No se encontraron productos"
- Si no hay categoría seleccionada: botón invisible
- Si no es proveedor (supplier_rank=0): sección invisible
- Actualización SOBRESCRIBE categoría existente (comportamiento deseado)
🧪 TESTS INCLUIDOS
✓ test_supplier_price_category_field
✓ test_action_update_products_opens_wizard
✓ test_wizard_product_count
✓ test_wizard_confirm_updates_products
✓ test_wizard_no_products_handling
Ejecutar:
docker-compose exec -T odoo odoo -d odoo \
-i product_price_category_supplier --test-enable --stop-after-init
📚 DOCUMENTACIÓN
✓ README.md - Guía completa de uso
✓ VALIDATION.md - Validación de componentes
✓ Docstrings en todos los métodos (inglés)
✓ Comentarios en code donde necesario
✓ Help text en campos
🚀 INSTALACIÓN
Opción 1: Instalar
docker-compose exec -T odoo odoo -d odoo \
-i product_price_category_supplier --stop-after-init
Opción 2: Actualizar (si ya existe)
docker-compose exec -T odoo odoo -d odoo \
-u product_price_category_supplier --stop-after-init
Opción 3: Con tests
docker-compose exec -T odoo odoo -d odoo \
-i product_price_category_supplier --test-enable --stop-after-init
✓ VERIFICACIONES REALIZADAS
Sintaxis Python:
✓ models/__init__.py
✓ models/res_partner.py
✓ models/wizard_update_product_category.py
✓ tests/__init__.py
✓ tests/test_product_price_category_supplier.py
Estructura:
✓ __manifest__.py con todas las dependencias
✓ __init__.py imports correctos
✓ Vistas XML bien formadas
✓ Archivo security/ir.model.access.csv con headers
✓ Traducciones PO con headers válidos
Convenciones:
✓ snake_case para nombres de modelos/campos
✓ Docstrings en inglés
✓ _() para traducción de strings user-facing
✓ No español en código
✓ Nombres descriptivos
📋 CHECKLIST PRE-INSTALACIÓN
Antes de instalar, verificar:
- [ ] Addon product_price_category está instalado (OCA)
- [ ] Addon product_pricelists_margins_custom está instalado
- [ ] Usuario tiene grupo sales_team.group_sale_manager
- [ ] Database "odoo" está activa
- [ ] Docker-compose está corriendo
DESPUÉS de instalar:
- [ ] Ir a Contactos → Proveedor
- [ ] Ver campo en pestaña Compras
- [ ] Seleccionar categoría
- [ ] Clic botón → abre wizard
- [ ] Confirmar → se actualiza
- [ ] Cambiar idioma a Español → verificar traducciones
- [ ] Cambiar idioma a Euskera → verificar traducciones
🎓 EXTENSIONES FUTURAS
1. Defaults automáticos al crear producto:
```python
@api.model_create_multi
def create(self, vals_list):
for vals in vals_list:
if vals.get('default_supplier_id') and not vals.get('price_category_id'):
supplier = self.env['res.partner'].browse(vals['default_supplier_id'])
if supplier.default_price_category_id:
vals['price_category_id'] = supplier.default_price_category_id.id
return super().create(vals_list)
```
2. Log de cambios en chatter del producto
3. Modal con preview de productos antes de actualizar
4. Botón de selección manual de productos
📞 SOPORTE
Documentación:
- README.md - Guía de uso completa
- VALIDATION.md - Validación de componentes
- Docstrings en código
Troubleshooting:
- Ver logs: docker-compose logs odoo
- Verificar permisos: Settings > Users & Companies > Groups
- Limpiar cache: Settings > Technical > Clear Caches
==============================================================
IMPLEMENTACIÓN FINALIZADA: 10 de febrero de 2026
Status: ✅ LISTO PARA INSTALAR
==============================================================

View file

@ -1,300 +0,0 @@
# INSTALLATION COMPLETE - product_price_category_supplier
**Status**: ✅ **ADDON SUCCESSFULLY INSTALLED**
**Date**: 10 de febrero de 2026
**Version**: 18.0.1.0.0
**License**: AGPL-3.0
---
## Quick Summary
El addon `product_price_category_supplier` ha sido creado, corregido y **instalado exitosamente** en tu instancia Odoo 18.0.
✅ **21 files created**
**3 files fixed** (XPath errors & translation issues)
✅ **0 remaining errors**
✅ **Database tables created**
**Translations loaded** (Spanish + Euskera)
---
## What Was Fixed
### Problem 1: ParseError en XPath
- **Issue**: Vista XML buscaba `//notebook/page[@name='purchase']` que no existe en Odoo 18
- **Solution**: Cambiado a `//page[@name='sales_purchases']` (estructura real)
- **File**: `views/res_partner_views.xml` (2 cambios)
### Problem 2: Translation Warnings
- **Issue**: Uso de `_()` en definiciones de campos causaba warnings al importar módulo
- **Solution**: Removidos `_()` de field definitions (se extraen automáticamente)
- **Files**:
- `models/res_partner.py` (1 cambio)
- `models/wizard_update_product_category.py` (4 cambios)
---
## Feature Verification
✅ **Field Added to Partner**
- Location: Supplier form → Sales & Purchase tab
- Field: "Default Price Category"
- Visibility: Only shows for suppliers (invisible="not supplier_rank")
✅ **Button Available**
- Label: "Apply to All Products"
- Opens wizard modal for confirmation
- Only visible when price category is selected
✅ **Tree View Column Hidden**
- Field appears in tree view but hidden by default
- Users can show/hide via column menu (column_invisible="1")
✅ **Wizard Functionality**
- Modal dialog for bulk update confirmation
- Shows: Supplier name, Price category, Product count
- Confirm/Cancel buttons
✅ **Security Configured**
- Group: `sales_team.group_sale_manager`
- Only sales managers can access wizard
✅ **Translations Available**
- Spanish (es): 20+ strings
- Euskera (eu): 20+ strings
---
## How to Use
### Step 1: Add Price Category to Supplier
```
1. Go to Contacts (Contactos)
2. Select a supplier (supplier_rank > 0)
3. Open "Sales & Purchase" tab
4. In "Price Category Settings" section:
- Select "Default Price Category" from dropdown
- Click "Apply to All Products"
```
### Step 2: Confirm Bulk Update
```
1. Modal appears showing:
- Supplier name
- Price category to apply
- Number of products to update
2. Click "Confirm" to proceed
3. Products updated successfully
```
### Step 3: Verify Update
```
1. Go to Products
2. Filter by supplier
3. Check "Price Category" field on each product
4. All should now have the selected category
```
---
## Architecture
### Models
- **res.partner**: Extended with `default_price_category_id` field
- **wizard.update.product.category**: Transient model for bulk update workflow
- **product.template**: Updated by wizard bulk action
### Views
- **res.partner form**: New group in "Sales & Purchase" tab
- **res.partner tree**: Hidden column (configurable)
- **wizard form**: Modal dialog with confirmation
### Security
- Restricted to `sales_team.group_sale_manager`
- Other users cannot see or use the wizard
### Dependencies
- `product_price_category` (OCA addon)
- `product_pricelists_margins_custom` (project addon)
- `sales_team` (Odoo core)
---
## File Structure
```
product_price_category_supplier/
├── __init__.py
├── __manifest__.py
├── models/
│ ├── __init__.py
│ ├── res_partner.py ← ✅ Fixed
│ └── wizard_update_product_category.py ← ✅ Fixed
├── views/
│ ├── res_partner_views.xml ← ✅ Fixed
│ └── wizard_update_product_category.xml
├── security/
│ └── ir.model.access.csv
├── tests/
│ ├── __init__.py
│ └── test_product_price_category_supplier.py
├── i18n/
│ ├── product_price_category_supplier.pot
│ ├── es.po
│ └── eu.po
├── README.md
├── VALIDATION.md
├── ERROR_FIX_REPORT.md ← 📄 NEW: Error report
└── IMPLEMENTACION_RESUMEN.txt
```
---
## Testing Commands
### Run Installation
```bash
cd /home/snt/Documentos/lab/odoo/kidekoop/odoo-addons
docker-compose exec -T odoo odoo -d odoo -i product_price_category_supplier --stop-after-init
```
### Run Tests
```bash
docker-compose exec -T odoo odoo -d odoo -i product_price_category_supplier --test-enable --stop-after-init
```
### Verify Syntax
```bash
python3 -m py_compile product_price_category_supplier/models/*.py
```
---
## Installation Output
```
2026-02-10 16:21:04,843 69 INFO odoo odoo.modules.loading:
loading product_price_category_supplier/security/ir.model.access.csv
2026-02-10 16:21:04,868 69 INFO odoo odoo.modules.loading:
loading product_price_category_supplier/views/res_partner_views.xml
2026-02-10 16:21:04,875 69 INFO odoo odoo.modules.loading:
loading product_price_category_supplier/views/wizard_update_product_category.xml
2026-02-10 16:21:04,876 69 INFO odoo odoo.addons.base.models.ir_module:
module product_price_category_supplier: loading translation file ...eu.po
2026-02-10 16:21:04,876 69 INFO odoo odoo.addons.base.models.ir_module:
module product_price_category_supplier: loading translation file ...es.po
2026-02-10 16:21:04,912 69 INFO odoo odoo.modules.loading:
Module product_price_category_supplier loaded in 0.68s, 179 queries
✅ No errors
✅ No critical warnings
✅ All translations loaded
```
---
## Next Steps
### 1. Test in UI (Optional)
- Open Odoo at http://localhost:8069
- Go to Contacts
- Select a supplier
- Test the new "Price Category Settings" section
### 2. Create Test Data (Optional)
```python
# In Odoo console
supplier = env['res.partner'].search([('supplier_rank', '>', 0)], limit=1)
category = env['product.price.category'].search([], limit=1)
supplier.default_price_category_id = category.id
supplier.action_update_products_price_category()
```
### 3. Change Language to Spanish/Euskera (Optional)
- User Preferences → Change Language
- Verify UI displays in chosen language
---
## Troubleshooting
### Issue: "ParseError in XPath"
**FIXED**: XPath now targets correct page name `sales_purchases`
### Issue: "No translation language detected" warning
**FIXED**: Removed `_()` from field definitions
### Issue: Wizard not opening
→ Check if user has `sales_team.group_sale_manager` permission
### Issue: Button not visible
→ Check if price category is selected (button has invisible="not default_price_category_id")
---
## Documentation
- 📄 [ERROR_FIX_REPORT.md](ERROR_FIX_REPORT.md) - Detailed error fixes
- 📄 [README.md](README.md) - Complete addon documentation
- 📄 [VALIDATION.md](VALIDATION.md) - Architecture validation
- 📄 [IMPLEMENTACION_RESUMEN.txt](IMPLEMENTACION_RESUMEN.txt) - Spanish summary
---
## Technical Details
### XPath Paths Used
**Form View**:
```xml
<xpath expr="//page[@name='sales_purchases']" position="inside">
```
Targets the Sales & Purchase tab in partner form, inserting our group inside it.
**Tree View**:
```xml
<field name="complete_name" position="after">
```
Targets the first field in the partner list view (complete_name, was: name).
### Translation System
Strings are extracted from:
1. Field `string` and `help` parameters (automatically)
2. XML button labels and strings
3. Python `_()` markers in logic
Files created:
- `product_price_category_supplier.pot` - Template
- `es.po` - Spanish translations
- `eu.po` - Euskera translations
---
## Support
If you need to:
1. **Modify button label**: Edit `views/res_partner_views.xml` line 16
2. **Change access group**: Edit `security/ir.model.access.csv`
3. **Adjust field visibility**: Edit `models/res_partner.py` line 15 (invisible condition)
4. **Update translations**: Edit `i18n/es.po` or `i18n/eu.po`
---
**Status**: ✅ Production Ready
**Created**: 10 de febrero de 2026
**License**: AGPL-3.0
**Author**: Criptomart

View file

@ -1,313 +0,0 @@
# ✅ ADDON INSTALLATION STATUS REPORT
**Addon**: `product_price_category_supplier`
**Status**: ✅ **INSTALLED & WORKING**
**Date**: 10 de febrero de 2026
**Installation Time**: 2 cycles (fixed errors on 2nd attempt)
---
## Executive Summary
El addon `product_price_category_supplier` fue creado exitosamente para extender Odoo 18.0 con funcionalidad de categorías de precio por proveedor.
**Ciclo 1**: Error ParseError en XPath (vista XML)
**Ciclo 2**: ✅ Errores corregidos, addon instalado correctamente
---
## Installation Timeline
### ❌ Attempt 1: Failed with ParseError
```
2026-02-10 16:17:56 - Addon installation started
2026-02-10 16:17:56 - WARNING: Translation not detected
2026-02-10 16:17:56 - ERROR: ParseError in XPath
2026-02-10 16:17:56 - CRITICAL: Failed to initialize database
Error: Element '<xpath expr="//notebook/page[@name='purchase']">' cannot be located
```
**Root Causes Identified**:
1. XPath path incorrect for Odoo 18 structure
2. `_()` calls in field definitions causing translation warnings
3. Tree view field name wrong
### ✅ Attempt 2: Successful Installation
```
2026-02-10 16:21:04 - Fixed XPath path (sales_purchases)
2026-02-10 16:21:04 - Removed _() from field definitions
2026-02-10 16:21:04 - Fixed tree view field name (complete_name)
2026-02-10 16:21:04 - ✅ Module loaded in 0.68s
2026-02-10 16:21:04 - ✅ 179 queries executed
2026-02-10 16:21:04 - ✅ Translations loaded (es, eu)
2026-02-10 16:21:04 - ✅ Installation complete
```
---
## File Status
### Code Files (All ✅ Fixed & Working)
| File | Purpose | Status | Changes |
|------|---------|--------|---------|
| `__manifest__.py` | Addon configuration | ✅ OK | - |
| `__init__.py` | Package init | ✅ OK | - |
| `models/res_partner.py` | Partner model extension | ✅ FIXED | 1 change |
| `models/wizard_update_product_category.py` | Wizard model | ✅ FIXED | 4 changes |
| `views/res_partner_views.xml` | Form & tree views | ✅ FIXED | 2 changes |
| `views/wizard_update_product_category.xml` | Wizard modal view | ✅ OK | - |
| `security/ir.model.access.csv` | Access control | ✅ OK | - |
| `tests/test_product_price_category_supplier.py` | Unit tests | ✅ OK | - |
### Documentation Files (All ✅ New)
| File | Purpose | Status |
|------|---------|--------|
| `ERROR_FIX_REPORT.md` | Detailed error analysis | ✅ NEW |
| `INSTALLATION_COMPLETE.md` | Installation summary | ✅ NEW |
| `BEFORE_AND_AFTER.md` | Visual error comparison | ✅ NEW |
| `README.md` | Feature documentation | ✅ Existing |
| `VALIDATION.md` | Architecture validation | ✅ Existing |
| `IMPLEMENTACION_RESUMEN.txt` | Spanish summary | ✅ Existing |
### Translation Files (All ✅ Complete)
| File | Language | Status | Strings |
|------|----------|--------|---------|
| `i18n/product_price_category_supplier.pot` | Template | ✅ Complete | 20+ |
| `i18n/es.po` | Spanish | ✅ Complete | 20+ |
| `i18n/eu.po` | Euskera | ✅ Complete | 20+ |
---
## Changes Made to Fix Errors
### Fix 1: XPath Path in Form View
```diff
- <xpath expr="//notebook/page[@name='purchase']" position="inside">
+ <xpath expr="//page[@name='sales_purchases']" position="inside">
```
**File**: `views/res_partner_views.xml` line 11
**Reason**: Odoo 18 partner form uses `sales_purchases` page name
### Fix 2: Field Name in Tree View
```diff
- <field name="name" position="after">
+ <field name="complete_name" position="after">
```
**File**: `views/res_partner_views.xml` line 27
**Reason**: Tree view uses `complete_name` as first field
### Fix 3: Remove _() from Partner Field
```diff
- string=_('Default Price Category'),
- help=_('Default price category for products from this supplier'),
+ string='Default Price Category',
+ help='Default price category for products from this supplier',
```
**File**: `models/res_partner.py` lines 13-15
**Reason**: Automatic extraction, `_()` causes translation warnings
### Fix 4: Remove _() from Wizard Fields
```diff
- string=_('Supplier'),
- string=_('Supplier Name'),
- string=_('Price Category'),
- string=_('Number of Products'),
+ string='Supplier',
+ string='Supplier Name',
+ string='Price Category',
+ string='Number of Products',
```
**File**: `models/wizard_update_product_category.py` lines 15, 21, 27, 34
**Reason**: Same as Fix 3 - automatic extraction by Odoo
---
## Verification Checklist
### ✅ Installation
- [x] Addon loads without errors
- [x] Database tables created
- [x] Security ACL registered
- [x] Views registered
- [x] Translations loaded
### ✅ Code Quality
- [x] Python syntax valid (py_compile)
- [x] XML well-formed
- [x] No linting errors
- [x] Project conventions followed
### ✅ Features
- [x] Field added to partner form
- [x] Field hidden in tree view
- [x] Button visible and functional
- [x] Wizard modal displays correctly
- [x] Permissions restricted to sales managers
### ✅ Translations
- [x] Spanish (es.po) loaded
- [x] Euskera (eu.po) loaded
- [x] 20+ strings per language
- [x] All msgid/msgstr pairs valid
---
## Addon Capabilities
### ✅ Features Implemented
1. **Supplier Default Price Category**
- Field added to res.partner
- Hidden for non-suppliers
- Configurable per supplier
2. **Bulk Product Update**
- Button to update all products
- Opens confirmation wizard
- Applies category to all supplier products
3. **Tree View Integration**
- Column hidden by default
- User can show/hide via column menu
- Configurable visibility
4. **Security**
- Wizard restricted to sales_team.group_sale_manager
- Other users cannot access
- Proper ACL configuration
5. **Internationalization**
- Spanish translation complete
- Euskera translation complete
- Field labels and messages translated
---
## Performance Metrics
### Installation
```
Module load time: 0.68 seconds
Database queries: 179
Status: ✅ Fast and efficient
```
### File Statistics
```
Total files: 24 (including __pycache__)
Source files: 14
Code lines: ~581
Test coverage: 5 unit tests
Documentation: 8 files (5 new)
Translations: 20+ strings per language
```
---
## Next Steps
### Immediate
1. ✅ Addon installed and working
2. ✅ All errors fixed
3. ✅ Documentation complete
### Optional Testing
1. Navigate to Contacts in Odoo UI
2. Select a supplier
3. Open Sales & Purchase tab
4. Test "Price Category Settings" section
5. Test "Apply to All Products" button
### Optional Configuration
1. Change user language to Spanish/Euskera
2. Verify UI displays correctly
3. Check button access permissions
---
## Documentation Structure
The addon now includes comprehensive documentation:
```
product_price_category_supplier/
├── ERROR_FIX_REPORT.md ..................... Detailed error analysis & fixes
├── INSTALLATION_COMPLETE.md ............... Installation summary
├── BEFORE_AND_AFTER.md .................... Visual error comparison
├── README.md ............................. Feature documentation
├── VALIDATION.md .......................... Architecture validation
├── IMPLEMENTACION_RESUMEN.txt ............. Spanish summary
└── [code files] ........................... Fully implemented addon
```
---
## Technical Details
### Architecture
- **Pattern**: Model inheritance + Transient wizard
- **Security**: Group-based ACL
- **Views**: XPath inheritance for forms and trees
- **Translations**: POT template + multi-language support
### Dependencies
- `product_price_category` (OCA addon)
- `product_pricelists_margins_custom` (project addon)
- `sales_team` (Odoo core)
### Database Tables
- `wizard_update_product_category` (new)
- `res_partner` (extended with field)
- `ir.ui.view` (2 new records)
- `ir.model.access` (1 new ACL)
---
## Support Information
### For Bugs or Issues
1. Check [ERROR_FIX_REPORT.md](ERROR_FIX_REPORT.md) for known issues
2. Review [BEFORE_AND_AFTER.md](BEFORE_AND_AFTER.md) for architecture
3. Consult [README.md](README.md) for usage
### For Modifications
1. Edit field visibility: `models/res_partner.py` line 15
2. Edit button label: `views/res_partner_views.xml` line 16
3. Edit access group: `security/ir.model.access.csv`
4. Edit translations: `i18n/es.po` or `i18n/eu.po`
---
## Conclusion
**Status**: ✅ **COMPLETE AND WORKING**
The addon has been:
- ✅ Created with full functionality
- ✅ Fixed of all installation errors
- ✅ Tested and verified working
- ✅ Documented comprehensively
- ✅ Installed successfully in Odoo 18.0
All requirements have been met:
- ✅ Extends `product_price_category` addon
- ✅ Field placed in Purchases tab (now: Sales & Purchase)
- ✅ Field hidden in tree view (configurable)
- ✅ Bulk update via wizard implemented
- ✅ Access restricted to sales_team.group_sale_manager
- ✅ Spanish and Euskera translations included
- ✅ Comprehensive tests included
The addon is **production-ready** and fully functional.
---
**Created**: 10 de febrero de 2026
**Status**: ✅ Installation Complete
**License**: AGPL-3.0
**Maintainer**: Criptomart

View file

@ -1,123 +0,0 @@
# QUICK REFERENCE - Fixes Applied
**Date**: 10 de febrero de 2026
**Addon**: product_price_category_supplier
**Status**: ✅ All fixed
---
## 3-Line Summary
| Issue | Fixed | File | Change |
|-------|-------|------|--------|
| **ParseError: XPath not found** | ✅ | `views/res_partner_views.xml` L11 | `purchase``sales_purchases` |
| **Tree field not found** | ✅ | `views/res_partner_views.xml` L27 | `name``complete_name` |
| **Translation warning (4 fields)** | ✅ | `models/wizard_update_product_category.py` L15,21,27,34 | Remove `_()` calls |
| **Translation warning (2 fields)** | ✅ | `models/res_partner.py` L13-15 | Remove `_()` calls |
---
## What Changed
### ✅ Fixed: Wrong XPath Path
```
OLD: <xpath expr="//notebook/page[@name='purchase']" position="inside">
NEW: <xpath expr="//page[@name='sales_purchases']" position="inside">
```
**Why**: Odoo 18 partner form doesn't have `purchase` page, it's called `sales_purchases`
---
### ✅ Fixed: Wrong Tree Field
```
OLD: <field name="name" position="after">
NEW: <field name="complete_name" position="after">
```
**Why**: Tree view (list) uses `complete_name`, not `name`
---
### ✅ Fixed: Translation Warnings (6 total)
**models/res_partner.py**:
```
OLD: string=_('Default Price Category'),
NEW: string='Default Price Category',
OLD: help=_('Default price category for products from this supplier'),
NEW: help='Default price category for products from this supplier',
```
**models/wizard_update_product_category.py**:
```
OLD: string=_('Supplier'),
NEW: string='Supplier',
OLD: string=_('Supplier Name'),
NEW: string='Supplier Name',
OLD: string=_('Price Category'),
NEW: string='Price Category',
OLD: string=_('Number of Products'),
NEW: string='Number of Products',
```
**Why**: Field strings are auto-extracted; `_()` causes "no translation language detected" warning
---
## Installation Commands
```bash
# Install addon
cd /home/snt/Documentos/lab/odoo/kidekoop/odoo-addons
docker-compose exec -T odoo odoo -d odoo -i product_price_category_supplier --stop-after-init
# Run tests
docker-compose exec -T odoo odoo -d odoo -i product_price_category_supplier --test-enable --stop-after-init
# Verify syntax
python3 -m py_compile product_price_category_supplier/models/*.py
```
---
## Files Modified Summary
| File | Lines | Changes |
|------|-------|---------|
| `views/res_partner_views.xml` | 42 | 2 fixes |
| `models/res_partner.py` | 45 | 1 fix |
| `models/wizard_update_product_category.py` | 77 | 4 fixes |
| **TOTAL** | **164** | **8 fixes** |
---
## Installation Result
```
✅ Module loaded in 0.68s, 179 queries
✅ 0 errors
✅ Translations loaded (es, eu)
✅ Database tables created
✅ Security ACL registered
```
---
## Documentation Added
- ✅ `ERROR_FIX_REPORT.md` - Detailed analysis
- ✅ `INSTALLATION_COMPLETE.md` - Summary
- ✅ `BEFORE_AND_AFTER.md` - Visual comparison
- ✅ `INSTALLATION_STATUS.md` - Full report
- ✅ This file - Quick reference
---
**Status**: ✅ Production Ready

View file

@ -1,152 +0,0 @@
#!/bin/bash
# QUICK START: product_price_category_supplier
# Guía de instalación rápida del addon
set -e
ADDON_DIR="/home/snt/Documentos/lab/odoo/kidekoop/odoo-addons/product_price_category_supplier"
echo "╔════════════════════════════════════════════════════════════════╗"
echo "║ QUICK START: product_price_category_supplier ║"
echo "║ Odoo 18.0 - Addon de Categoría de Precio para Proveedores ║"
echo "╚════════════════════════════════════════════════════════════════╝"
echo ""
# Verificar que el addon existe
if [ ! -d "$ADDON_DIR" ]; then
echo "❌ Error: Addon no encontrado en $ADDON_DIR"
exit 1
fi
echo "✓ Addon ubicado: $ADDON_DIR"
echo ""
# Mostrar opciones
echo "┌────────────────────────────────────────────────────────────────┐"
echo "│ OPCIONES DE INSTALACIÓN │"
echo "└────────────────────────────────────────────────────────────────┘"
echo ""
echo "1. INSTALAR el addon (primera vez)"
echo " docker-compose exec -T odoo odoo -d odoo \\"
echo " -i product_price_category_supplier --stop-after-init"
echo ""
echo "2. ACTUALIZAR el addon (si ya existe)"
echo " docker-compose exec -T odoo odoo -d odoo \\"
echo " -u product_price_category_supplier --stop-after-init"
echo ""
echo "3. EJECUTAR TESTS"
echo " docker-compose exec -T odoo odoo -d odoo \\"
echo " -i product_price_category_supplier --test-enable --stop-after-init"
echo ""
echo "4. VER CAMBIOS EN INTERFAZ"
echo " - Ir a Partners (Contactos)"
echo " - Seleccionar un proveedor"
echo " - Abrir pestaña 'Compras'"
echo " - Buscar sección 'Price Category Settings'"
echo ""
# Menú interactivo
echo "┌────────────────────────────────────────────────────────────────┐"
echo "│ SELECCIONAR ACCIÓN │"
echo "└────────────────────────────────────────────────────────────────┘"
echo ""
echo "¿Qué deseas hacer?"
echo " [1] Instalar addon"
echo " [2] Actualizar addon"
echo " [3] Ejecutar tests"
echo " [4] Ver documentación"
echo " [5] Salir"
echo ""
read -p "Selecciona una opción (1-5): " choice
case $choice in
1)
echo ""
echo "📦 INSTALANDO addon..."
echo ""
echo "docker-compose exec -T odoo odoo -d odoo \\"
echo " -i product_price_category_supplier --stop-after-init"
echo ""
echo "⏳ Ejecuta el comando anterior en tu terminal Docker"
echo ""
;;
2)
echo ""
echo "🔄 ACTUALIZANDO addon..."
echo ""
echo "docker-compose exec -T odoo odoo -d odoo \\"
echo " -u product_price_category_supplier --stop-after-init"
echo ""
echo "⏳ Ejecuta el comando anterior en tu terminal Docker"
echo ""
;;
3)
echo ""
echo "🧪 EJECUTANDO tests..."
echo ""
echo "docker-compose exec -T odoo odoo -d odoo \\"
echo " -i product_price_category_supplier --test-enable --stop-after-init"
echo ""
echo "⏳ Ejecuta el comando anterior en tu terminal Docker"
echo ""
;;
4)
echo ""
echo "📚 DOCUMENTACIÓN DISPONIBLE:"
echo ""
if [ -f "$ADDON_DIR/README.md" ]; then
echo "✓ README.md - Guía completa de uso"
echo " $(head -3 $ADDON_DIR/README.md | tail -1)"
echo ""
fi
if [ -f "$ADDON_DIR/VALIDATION.md" ]; then
echo "✓ VALIDATION.md - Validación de componentes"
echo " Contiene checklist completo de verificación"
echo ""
fi
if [ -f "$ADDON_DIR/IMPLEMENTACION_RESUMEN.txt" ]; then
echo "✓ IMPLEMENTACION_RESUMEN.txt - Resumen ejecutivo"
echo " Descripción de funcionalidades y componentes"
echo ""
fi
echo "Abre estos archivos para más información"
echo ""
;;
5)
echo "👋 Saliendo..."
exit 0
;;
*)
echo "❌ Opción no válida"
exit 1
;;
esac
echo "╔════════════════════════════════════════════════════════════════╗"
echo "║ INFORMACIÓN ÚTIL ║"
echo "╚════════════════════════════════════════════════════════════════╝"
echo ""
echo "📂 Ubicación del addon:"
echo " $ADDON_DIR"
echo ""
echo "📦 Dependencias:"
echo " • product_price_category (OCA addon)"
echo " • product_pricelists_margins_custom (del proyecto)"
echo " • sales_team (Odoo core)"
echo ""
echo "🔐 Permisos requeridos:"
echo " • sales_team.group_sale_manager"
echo ""
echo "🌍 Idiomas soportados:"
echo " • Español (es.po)"
echo " • Euskera (eu.po)"
echo ""
echo "📝 Ver logs en Docker:"
echo " docker-compose logs odoo | tail -50"
echo ""
echo "💾 Acceder a base de datos:"
echo " docker-compose exec -T postgres psql -U odoo -d odoo"
echo ""
echo "✅ Listo! Si necesitas más ayuda, consulta la documentación en el addon."
echo ""

View file

@ -1,312 +0,0 @@
# TEST REPORT - product_price_category_supplier
**Date**: 10 de febrero de 2026
**Status**: ✅ ALL TESTS PASSING
**Test Framework**: Odoo TransactionCase
**Test Count**: 10 comprehensive tests
---
## Executive Summary
**10/10 tests passing** (0 failures, 0 errors)
⏱️ **Execution time**: 0.35 seconds
📊 **Database queries**: 379 queries
🎯 **Coverage**: All critical features tested
---
## Test Suite Overview
### Test Class: `TestProductPriceCategorySupplier`
**Purpose**: Comprehensive testing of supplier price category functionality
**Test Data Setup**:
- 2 price categories (Premium, Standard)
- 2 suppliers with categories assigned
- 1 customer (non-supplier)
- 5 products (3 from Supplier A, 1 from Supplier B, 1 without supplier)
---
## Test Cases
### ✅ Test 01: Supplier Has Default Price Category Field
**Purpose**: Verify field existence and assignment
**Status**: PASSED
**Verifies**:
- `default_price_category_id` field exists on res.partner
- Supplier can have category assigned
- Field value persists correctly
---
### ✅ Test 02: Action Opens Wizard
**Purpose**: Test wizard opening action
**Status**: PASSED
**Verifies**:
- Action type is `ir.actions.act_window`
- Opens `wizard.update.product.category` model
- Target is `new` (modal dialog)
- Returns valid wizard `res_id`
---
### ✅ Test 03: Wizard Counts Products Correctly
**Purpose**: Verify product counting logic
**Status**: PASSED
**Verifies**:
- Wizard shows correct product count (3 for Supplier A)
- Partner name displays correctly
- Price category matches supplier's default
---
### ✅ Test 04: Wizard Updates All Products
**Purpose**: Test bulk update functionality
**Status**: PASSED
**Verifies**:
- All products from supplier get updated
- Products from other suppliers remain unchanged
- Success notification displayed
- Database writes work correctly
**Flow Tested**:
```
Initial: Products 1,2,3 have no category
Action: Wizard confirms bulk update to Premium
Result: Products 1,2,3 now have Premium category
Product 4 (Supplier B) unchanged
```
---
### ✅ Test 05: Wizard Handles No Products
**Purpose**: Test edge case - supplier with no products
**Status**: PASSED
**Verifies**:
- Warning notification displayed
- No database errors
- Graceful handling of empty result
---
### ✅ Test 06: Customer Field Visibility
**Purpose**: Verify customers don't see price category
**Status**: PASSED
**Verifies**:
- Customer has `supplier_rank = 0`
- No price category assigned to customer
- Logic for field visibility works correctly
---
### ✅ Test 07: Wizard Overwrites Existing Categories
**Purpose**: Test update behavior on pre-existing categories
**Status**: PASSED
**Verifies**:
- Existing categories get overwritten
- No data loss or corruption
- Update is complete (all products updated)
**Flow Tested**:
```
Initial: Products have Standard category
Action: Update to Premium via wizard
Result: All products now Premium (overwritten)
```
---
### ✅ Test 08: Multiple Suppliers Independent Updates
**Purpose**: Test isolation between suppliers
**Status**: PASSED
**Verifies**:
- Updating Supplier A doesn't affect Supplier B products
- Each supplier maintains independent category
- No cross-contamination of data
**Flow Tested**:
```
Supplier A products → Premium
Supplier B products → Standard
Both remain independent after updates
```
---
### ✅ Test 09: Wizard Readonly Fields
**Purpose**: Verify display field computations
**Status**: PASSED
**Verifies**:
- `partner_name` computed from `partner_id.name`
- Related fields work correctly
- Display data matches source
---
### ✅ Test 10: Action Counts Products Correctly
**Purpose**: Verify product count accuracy
**Status**: PASSED
**Verifies**:
- Manual count matches wizard count
- Search logic is correct
- Count updates when products change
---
## Test Execution Results
```
2026-02-10 16:40:38,977 1 INFO odoo odoo.tests.stats:
product_price_category_supplier: 12 tests 0.35s 379 queries
2026-02-10 16:40:38,977 1 INFO odoo odoo.tests.result:
0 failed, 0 error(s) of 10 tests when loading database 'odoo'
✅ Result: ALL TESTS PASSED
```
---
## Coverage Analysis
### ✅ Features Fully Tested
| Feature | Tests Covering It |
|---------|-------------------|
| **Field existence** | test_01 |
| **Wizard opening** | test_02 |
| **Product counting** | test_03, test_10 |
| **Bulk updates** | test_04, test_07, test_08 |
| **Edge cases** | test_05 |
| **Visibility logic** | test_06 |
| **Data isolation** | test_08 |
| **Related fields** | test_09 |
### ✅ Code Paths Covered
- Partner field assignment ✅
- Wizard creation and display ✅
- Bulk product updates ✅
- Empty supplier handling ✅
- Multi-supplier scenarios ✅
- Category overwrites ✅
---
## Performance Metrics
| Metric | Value | Analysis |
|--------|-------|----------|
| **Total execution time** | 0.35s | ✅ Fast (< 1s) |
| **Database queries** | 379 | ✅ Efficient for 10 tests |
| **Average per test** | 37.9 queries | ✅ Reasonable |
| **Setup time** | Included | One-time cost |
---
## Test Data Structure
### Created Records
```python
# Categories
category_premium = "Premium"
category_standard = "Standard"
# Suppliers
supplier_a (rank=1, category=Premium)
├── product_1
├── product_2
└── product_3
supplier_b (rank=1, category=Standard)
└── product_4
# Customers
customer_a (rank=0, supplier_rank=0)
# Orphan Products
product_5 (no supplier)
```
---
## Running the Tests
### Command
```bash
cd /home/snt/Documentos/lab/odoo/kidekoop/odoo-addons
docker-compose run --rm odoo odoo -d odoo \
-u product_price_category_supplier \
--test-enable \
--stop-after-init
```
### Expected Output
```
INFO odoo odoo.tests.stats: product_price_category_supplier: 12 tests 0.35s 379 queries
INFO odoo odoo.tests.result: 0 failed, 0 error(s) of 10 tests
```
---
## Test Isolation
All tests use `TransactionCase` which ensures:
- ✅ Each test runs in isolated transaction
- ✅ Database rollback after each test
- ✅ No test affects another
- ✅ Clean state for each test
---
## Code Quality Indicators
**No test flakiness** - All tests pass consistently
**Fast execution** - 0.35s for full suite
**Good coverage** - All major features tested
**Edge cases handled** - Empty suppliers, overwrites, isolation
**Clear assertions** - Descriptive error messages
---
## Future Test Recommendations
### Optional Enhancements
1. **Permission tests**: Verify `sales_team.group_sale_manager` restriction
2. **UI tests**: Test form view visibility and button behavior
3. **Translation tests**: Verify Spanish/Euskera strings load correctly
4. **Concurrency tests**: Multiple users updating same supplier
5. **Performance tests**: Bulk updates with 1000+ products
### Not Critical
These are optional enhancements for extended testing but current coverage is production-ready.
---
## Conclusion
**Status**: ✅ **PRODUCTION READY**
The test suite provides comprehensive coverage of all addon features:
- Field assignment and persistence
- Wizard functionality and UI
- Bulk product updates
- Edge cases and error handling
- Data isolation and integrity
- Performance within acceptable bounds
All 10 tests passing with 0 failures and 0 errors confirms the addon is stable and ready for deployment.
---
**Maintained by**: Criptomart
**License**: AGPL-3.0
**Last Updated**: 10 de febrero de 2026

View file

@ -1,309 +0,0 @@
# Validación del Addon: product_price_category_supplier
## ✅ Estructura del Addon
```
product_price_category_supplier/
├── __init__.py ✓ Root module init
├── __manifest__.py ✓ Addon metadata
├── models/
│ ├── __init__.py ✓ Models package init
│ ├── res_partner.py ✓ Partner extension with field + method
│ └── wizard_update_product_category.py ✓ Transient wizard model
├── views/
│ ├── res_partner_views.xml ✓ Partner form/tree extensions
│ └── wizard_update_product_category.xml ✓ Wizard form view
├── security/
│ └── ir.model.access.csv ✓ Model access control
├── tests/
│ ├── __init__.py ✓ Tests package init
│ └── test_product_price_category_supplier.py ✓ Unit tests
├── i18n/
│ ├── product_price_category_supplier.pot ✓ Translation template
│ ├── es.po ✓ Spanish translations
│ └── eu.po ✓ Basque translations
├── README.md ✓ Addon documentation
└── install_addon.sh ✓ Installation helper script
```
## ✅ Componentes Implementados
### 1. **Modelos (models/)**
#### res_partner.py
- ✓ Extiende `res.partner`
- ✓ Campo: `default_price_category_id` (Many2one → product.price.category)
- ✓ Método: `action_update_products_price_category()` - Abre wizard modal
- ✓ Docstrings en inglés
- ✓ Uso correcto de `_()` para traducción
#### wizard_update_product_category.py
- ✓ Modelo Transient (TransientModel)
- ✓ Campo `partner_id` - Readonly
- ✓ Campo `partner_name` - Readonly, related
- ✓ Campo `price_category_id` - Readonly
- ✓ Campo `product_count` - Readonly, informativo
- ✓ Método: `action_confirm()` - Bulk update de productos
- ✓ Manejo de caso vacío (sin productos)
- ✓ Retorna notificaciones client-side
- ✓ Docstrings en inglés
### 2. **Vistas (views/)**
#### res_partner_views.xml
- ✓ Vista Form
- Hereda de `base.view_partner_form`
- XPath en página "Purchases"
- Grupo "Price Category Settings" con condición `invisible="not supplier_rank"`
- Campo `default_price_category_id`
- Botón "Apply to All Products" con condición invisible
- Clase CSS `btn-primary` para destacar
- Help text descriptivo
- ✓ Vista Tree
- Hereda de `base.view_partner_tree`
- Campo oculto con `column_invisible="1"`
- Configurable manualmente desde menú de columnas del usuario
#### wizard_update_product_category.xml
- ✓ Vista Form (modal)
- Alert box con información de confirmación
- Campos readonly y related
- Footer con botones:
- Confirm (btn-primary)
- Cancel (btn-secondary)
### 3. **Seguridad (security/)**
#### ir.model.access.csv
- ✓ Modelo: `wizard.update.product.category`
- ✓ Grupo: `sales_team.group_sale_manager`
- ✓ Permisos: read, write, create, unlink todos en True
- ✓ Acceso restringido a gestores de ventas
### 4. **Internacionalización (i18n/)**
#### product_price_category_supplier.pot (Template)
- ✓ Header con metadata
- ✓ Strings para traducción:
- Field labels
- Field help text
- Button labels
- Group titles
- View strings
- Action messages
- ✓ Comentarios con ubicación de strings
#### es.po (Spanish)
- ✓ Header con Language: es
- ✓ Todas las traducciones al español
- ✓ Términos consistentes:
- "Proveedor" (Supplier)
- "Categoría de Precio" (Price Category)
- "Producto(s)" (Products)
#### eu.po (Basque/Euskera)
- ✓ Header con Language: eu
- ✓ Todas las traducciones al euskera
- ✓ Terminología en vasco:
- "Hornitzailea" (Supplier)
- "Prezioak Kategoria" (Price Category)
- "Produktu" (Products)
### 5. **Tests (tests/)**
#### test_product_price_category_supplier.py
- ✓ TransactionCase para tests con BD
- ✓ setUp con datos de prueba
- ✓ Test: crear campo en partner
- ✓ Test: abrir wizard
- ✓ Test: contar productos en wizard
- ✓ Test: ejecutar acción confirmar
- ✓ Test: verificar actualización en bulk
- ✓ Test: verificar no afecta otros productos
- ✓ Test: manejo de caso sin productos
### 6. **Documentación**
#### README.md
- ✓ Descripción completa
- ✓ Funcionalidades listadas
- ✓ Dependencias documentadas
- ✓ Instrucciones de instalación
- ✓ Flujo de uso paso a paso
- ✓ Campos documentados
- ✓ Modelos explicados
- ✓ Vistas descritas
- ✓ Seguridad documentada
- ✓ Comportamiento explicado
- ✓ Extensión futura sugerida
- ✓ Testing instructions
### 7. **Manifest**
#### __manifest__.py
- ✓ Nombre descriptivo
- ✓ Versión Odoo 18.0
- ✓ Categoría Product
- ✓ Summary conciso
- ✓ Descripción completa
- ✓ Author y License AGPL-3
- ✓ Dependencies correcto:
- `product_price_category` (OCA addon base)
- `product_pricelists_margins_custom` (Addon del proyecto)
- `sales_team` (Para grupo de permisos)
- ✓ Data files listados
- ✓ I18n files listados
## ✅ Convenciones del Proyecto
### Código Python
- ✓ PEP8 compliance
- ✓ snake_case para nombres
- ✓ Docstrings en inglés
- ✓ Uso correcto de `_()` para traducción
- ✓ No abreviaturas crípticas
- ✓ Nombres descriptivos
- ✓ Import organizados
### XML/Vistas
- ✓ XPath para herencia
- ✓ Campo oculto con column_invisible=1
- ✓ Condiciones con invisible=
- ✓ Clases CSS btn-primary/btn-secondary
- ✓ Strings sin `_()` en QWeb (Odoo los extrae automáticamente)
- ✓ Estructura clara y legible
### Traducciones
- ✓ Format PO válido
- ✓ Headers en español y euskera
- ✓ Todos los strings con msgid/msgstr
- ✓ Comentarios en inglés (#. module:)
## ✅ Funcionalidad
### Flujo Principal
1. Abrir partner proveedor
2. Llenar `default_price_category_id`
3. Clic botón "Aplicar a Todos los Productos"
4. Se abre wizard modal
5. Muestra información de confirmación
6. Clic "Confirmar"
7. Bulk update de todos los productos del proveedor
8. Notificación de éxito
### Seguridad
- ✓ Solo usuarios de `sales_team.group_sale_manager` pueden:
- Crear wizard records
- Leer wizard records
- Escribir wizard records
- Borrar wizard records
### Validaciones
- ✓ Wizard valida que exista `price_category_id`
- ✓ Wizard busca productos por `default_supplier_id`
- ✓ Retorna warning si no hay productos
- ✓ Retorna success con count si actualización exitosa
## ✅ Compatibilidad
### Dependencias Externas
- ✓ product_price_category (OCA, Odoo 18.0)
- Proporciona modelo `product.price.category`
- Proporciona extensión de `product.template` con `price_category_id`
- ✓ product_pricelists_margins_custom (Addon del proyecto)
- Extiende product.template con campos adicionales
- Sin conflictos con nuestras extensiones
### Herencia
- ✓ res.partner: herencia limpia, solo añade campo
- ✓ Ningún conflicto con otras extensiones
- ✓ Modelos Transient sin herencia (modelo nuevo)
## ✅ Instalación Verificada
### Pre-requisitos
- ✓ Odoo 18.0+
- ✓ Database "odoo" activa
- ✓ Addon `product_price_category` instalado (OCA)
- ✓ Addon `product_pricelists_margins_custom` instalado (proyecto)
- ✓ Usuario con grupo `sales_team.group_sale_manager`
### Pasos Instalación
```bash
# Modo 1: Instalar addon
docker-compose exec -T odoo odoo -d odoo -i product_price_category_supplier --stop-after-init
# Modo 2: Actualizar addon (si ya existe)
docker-compose exec -T odoo odoo -d odoo -u product_price_category_supplier --stop-after-init
# Modo 3: Con tests
docker-compose exec -T odoo odoo -d odoo -i product_price_category_supplier --test-enable --stop-after-init
```
## ✅ Testing
### Tests Unitarios Incluidos
- `test_supplier_price_category_field()` - Verifica campo existe
- `test_action_update_products_opens_wizard()` - Verifica acción abre wizard
- `test_wizard_product_count()` - Verifica conteo de productos
- `test_wizard_confirm_updates_products()` - Verifica bulk update
- `test_wizard_no_products_handling()` - Verifica caso sin productos
### Ejecutar Tests
```bash
docker-compose exec -T odoo odoo -d odoo \
-i product_price_category_supplier \
--test-enable --stop-after-init
```
## 📋 Checklist Final
- [x] Estructura completa creada
- [x] Modelos implementados
- [x] Vistas creadas
- [x] Seguridad configurada
- [x] Traducciones agregadas (es, eu)
- [x] Tests escritos
- [x] README documentado
- [x] Manifest correcto
- [x] Convenciones del proyecto seguidas
- [x] Sin errores de sintaxis
- [x] Listo para instalar
## 🚀 Siguientes Pasos
1. **Instalar en Docker**:
```bash
docker-compose exec -T odoo odoo -d odoo -i product_price_category_supplier --stop-after-init
```
2. **Verificar en UI**:
- Ir a Partners (Contactos)
- Seleccionar un proveedor
- Ver campo "Categoría de Precio por Defecto" en pestaña Compras
- Seleccionar categoría y clic botón
- Confirmar en wizard
3. **Ejecutar Tests**:
```bash
docker-compose exec -T odoo odoo -d odoo \
-i product_price_category_supplier --test-enable --stop-after-init
```
4. **Cambiar idioma** y verificar traducciones:
- Preferences → Language → Spanish / Basque
- Verificar que los labels y mensajes se muestren traducidos
---
**Status**: ✅ **IMPLEMENTACIÓN COMPLETA**
**Fecha**: 10 de febrero de 2026
**Licencia**: AGPL-3.0

View file

@ -1,69 +0,0 @@
#!/bin/bash
# Install and verify product_price_category_supplier addon
# Usage: ./install_addon.sh
set -e
ADDON_NAME="product_price_category_supplier"
WORKSPACE="/home/snt/Documentos/lab/odoo/kidekoop/odoo-addons"
echo "=========================================="
echo "Installing: $ADDON_NAME"
echo "=========================================="
# Check if addon directory exists
if [ ! -d "$WORKSPACE/$ADDON_NAME" ]; then
echo "❌ Error: Addon directory not found at $WORKSPACE/$ADDON_NAME"
exit 1
fi
# Check required files
echo "✓ Checking required files..."
required_files=(
"__manifest__.py"
"__init__.py"
"models/__init__.py"
"models/res_partner.py"
"models/wizard_update_product_category.py"
"views/res_partner_views.xml"
"views/wizard_update_product_category.xml"
"security/ir.model.access.csv"
"README.md"
)
for file in "${required_files[@]}"; do
if [ ! -f "$WORKSPACE/$ADDON_NAME/$file" ]; then
echo "❌ Missing file: $file"
exit 1
fi
echo "$file"
done
echo ""
echo "✓ All required files found!"
# Display addon info
echo ""
echo "=========================================="
echo "Addon Information"
echo "=========================================="
echo "Name: $ADDON_NAME"
echo "Location: $WORKSPACE/$ADDON_NAME"
echo "Version: $(grep -oP "\"version\": \"\K[^\"]*" $WORKSPACE/$ADDON_NAME/__manifest__.py)"
echo "Dependencies: $(grep -oP "'depends': \[\K[^\]]*" $WORKSPACE/$ADDON_NAME/__manifest__.py)"
# Ready to install
echo ""
echo "=========================================="
echo "Ready to Install"
echo "=========================================="
echo ""
echo "Run in Docker:"
echo " docker-compose exec -T odoo odoo -d odoo -u $ADDON_NAME --stop-after-init"
echo ""
echo "Or install with dependencies:"
echo " docker-compose exec -T odoo odoo -d odoo -i $ADDON_NAME --stop-after-init"
echo ""
echo "Run tests:"
echo " docker-compose exec -T odoo odoo -d odoo -i $ADDON_NAME --test-enable --stop-after-init"
echo ""

View file

@ -1,156 +0,0 @@
#!/bin/bash
# Script para ejecutar tests de website_sale_aplicoop
echo "=========================================="
echo "Ejecutando tests de website_sale_aplicoop"
echo "=========================================="
# Ejecutar tests específicos de precios
docker-compose exec -T odoo odoo shell -c /etc/odoo/odoo.conf -d odoo << 'PYTHON_SCRIPT'
import logging
_logger = logging.getLogger(__name__)
# Cargar el módulo
env = self.env
# Test 1: Verificar que el método _compute_price_with_taxes existe
print("\n=== Test 1: Verificar método _compute_price_with_taxes ===")
try:
from odoo.addons.website_sale_aplicoop.controllers.website_sale import AplicoopWebsiteSale
controller = AplicoopWebsiteSale()
print("✓ Método _compute_price_with_taxes encontrado")
print(f" Firma: {controller._compute_price_with_taxes.__doc__}")
except Exception as e:
print(f"✗ Error: {e}")
# Test 2: Crear producto con impuesto y verificar cálculo
print("\n=== Test 2: Calcular precio con impuesto 21% ===")
product = None
try:
# Obtener o crear impuesto
tax_21 = env['account.tax'].search([
('amount', '=', 21.0),
('type_tax_use', '=', 'sale'),
('company_id', '=', env.company.id)
], limit=1)
if not tax_21:
# Crear tax group si no existe
country_es = env.ref('base.es', raise_if_not_found=False)
if not country_es:
country_es = env['res.country'].search([('code', '=', 'ES')], limit=1)
tax_group = env['account.tax.group'].search([
('company_id', '=', env.company.id),
('country_id', '=', country_es.id if country_es else False)
], limit=1)
if not tax_group:
tax_group = env['account.tax.group'].create({
'name': 'IVA',
'company_id': env.company.id,
'country_id': country_es.id if country_es else False,
})
'name': 'IVA 21% Test',
'amount': 21.0,
'amount_type': 'percent',
'type_tax_use': 'sale',
'price_include': False,
'company_id': env.company.id,
'country_id': country_es.id if country_es else False,
'tax_group_id': tax_group.id,
})
print(f" Impuesto creado: {tax_21.name}")
else:
print(f" Impuesto encontrado: {tax_21.name}")
# Crear producto de prueba
product = env['product.product'].search([
('name', '=', 'Test Product Tax Calculation')
], limit=1)
if not product:
product = env['product.product'].create({
'name': 'Test Product Tax Calculation',
'list_price': 100.0,
'taxes_id': [(6, 0, [tax_21.id])],
'company_id': env.company.id,
})
print(f" Producto creado: {product.name}")
else:
print(f" Producto encontrado: {product.name}")
# Calcular precio con impuestos
base_price = 100.0
taxes = product.taxes_id.filtered(
lambda t: t.company_id == env.company
)
if taxes:
tax_result = taxes.compute_all(
base_price,
currency=env.company.currency_id,
quantity=1.0,
product=product,
)
price_with_tax = tax_result['total_included']
price_without_tax = tax_result['total_excluded']
print(f" Precio base: {base_price:.2f} €")
print(f" Precio sin impuestos: {price_without_tax:.2f} €")
print(f" Precio con impuestos: {price_with_tax:.2f} €")
print(f" Impuesto aplicado: {price_with_tax - price_without_tax:.2f} €")
if abs(price_with_tax - 121.0) < 0.01:
print("✓ Test PASADO: 100 + 21% = 121.00")
else:
print(f"✗ Test FALLADO: Esperado 121.00, obtenido {price_with_tax:.2f}")
else:
print("✗ Producto sin impuestos configurados")
except Exception as e:
print(f"✗ Error en test: {e}")
import traceback
traceback.print_exc()
# Test 3: Verificar comportamiento de OCA _get_price
print("\n=== Test 3: Verificar OCA _get_price ===")
if product:
try:
pricelist = env['product.pricelist'].search([
('company_id', '=', env.company.id)
], limit=1)
if not pricelist:
pricelist = env['product.pricelist'].create({
'name': 'Test Pricelist',
'company_id': env.company.id,
})
price_info = product._get_price(
qty=1.0,
pricelist=pricelist,
fposition=False,
)
print(f" Precio OCA (value): {price_info.get('value', 0):.2f} €")
print(f" Tax included: {price_info.get('tax_included', False)}")
print(f" Original value: {price_info.get('original_value', 0):.2f} €")
print(f" Discount: {price_info.get('discount', 0):.1f}%")
if abs(price_info['value'] - 100.0) < 0.01:
print("✓ OCA retorna precio base SIN impuestos (esperado)")
else:
print(f"✗ OCA debería retornar 100.0, retornó {price_info['value']:.2f}")
except Exception as e:
print(f"✗ Error en test OCA: {e}")
import traceback
traceback.print_exc()
else:
print("✗ Test 3 omitido: producto no creado en Test 2")
# Hacer commit para que los cambios persistan
env.cr.commit()
PYTHON_SCRIPT

View file

@ -1,197 +0,0 @@
#!/usr/bin/env python3
"""
Script de prueba para verificar que los precios incluyen impuestos.
Se ejecuta dentro del contenedor de Odoo.
"""
import logging
import os
import sys
# Agregar path de Odoo
sys.path.insert(0, "/usr/lib/python3/dist-packages")
import odoo # noqa: E402
from odoo import SUPERUSER_ID # noqa: E402
from odoo import api # noqa: E402
logger = logging.getLogger(__name__)
# Configurar Odoo
odoo.tools.config["db_host"] = os.environ.get("HOST", "db")
odoo.tools.config["db_port"] = int(os.environ.get("PORT", 5432))
odoo.tools.config["db_user"] = os.environ.get("USER", "odoo")
odoo.tools.config["db_password"] = os.environ.get("PASSWORD", "odoo")
logger.info("\n" + "=" * 60)
logger.info("TEST: Precios con impuestos incluidos")
logger.info("=" * 60 + "\n")
try:
db_name = "odoo"
registry = odoo.registry(db_name)
with registry.cursor() as cr:
env = api.Environment(cr, SUPERUSER_ID, {})
logger.info(f"✓ Conectado a BD: {db_name}")
logger.info(f" Usuario: {env.user.name}")
logger.info(f" Compañía: {env.company.name}\n")
# Test 1: Verificar módulo
logger.info("TEST 1: Verificar módulo instalado")
logger.info("-" * 60)
module = env["ir.module.module"].search(
[("name", "=", "website_sale_aplicoop")], limit=1
)
if module and module.state == "installed":
logger.info("✓ Módulo website_sale_aplicoop instalado")
else:
logger.error("✗ Módulo NO instalado")
sys.exit(1)
# Test 2: Verificar método nuevo
logger.info("\nTEST 2: Verificar método _compute_price_with_taxes")
logger.info("-" * 60)
try:
from odoo.addons.website_sale_aplicoop.controllers.website_sale import (
AplicoopWebsiteSale,
)
controller = AplicoopWebsiteSale()
if hasattr(controller, "_compute_price_with_taxes"):
logger.info("✓ Método _compute_price_with_taxes existe")
import inspect
sig = inspect.signature(controller._compute_price_with_taxes)
logger.info(f" Firma: {sig}")
else:
logger.error("✗ Método NO encontrado")
except Exception as e:
logger.exception("✗ Error verificando método: %s", e)
# Test 3: Probar cálculo de impuestos
logger.info("\nTEST 3: Calcular precio con impuestos")
logger.info("-" * 60)
# Buscar un producto con impuestos
product = env["product.product"].search(
[("sale_ok", "=", True), ("taxes_id", "!=", False)], limit=1
)
if not product:
logger.info(" Creando producto de prueba...")
# Buscar impuesto existente
tax = env["account.tax"].search(
[("type_tax_use", "=", "sale"), ("company_id", "=", env.company.id)],
limit=1,
)
if tax:
product = env["product.product"].create(
{
"name": "Test Product With Tax",
"list_price": 100.0,
"taxes_id": [(6, 0, [tax.id])],
"sale_ok": True,
}
)
logger.info(f" Producto creado: {product.name}")
else:
logger.error(" ✗ No hay impuestos de venta configurados")
sys.exit(1)
else:
logger.info(f" Producto encontrado: {product.name}")
logger.info(f" Precio de lista: {product.list_price:.2f}")
taxes = product.taxes_id.filtered(lambda t: t.company_id == env.company)
if taxes:
logger.info(
" Impuestos: %s",
", ".join(f"{t.name} ({t.amount}%)" for t in taxes),
)
# Calcular precio con impuestos
base_price = product.list_price
tax_result = taxes.compute_all(
base_price,
currency=env.company.currency_id,
quantity=1.0,
product=product,
)
price_without_tax = tax_result["total_excluded"]
price_with_tax = tax_result["total_included"]
tax_amount = price_with_tax - price_without_tax
logger.info("\n Cálculo:")
logger.info(f" Base: {base_price:.2f}")
logger.info(f" Sin IVA: {price_without_tax:.2f}")
logger.info(f" IVA: {tax_amount:.2f}")
logger.info(f" CON IVA: {price_with_tax:.2f}")
if price_with_tax > price_without_tax:
logger.info(
"\n ✓ PASADO: Precio con IVA (%.2f) > sin IVA (%.2f)",
price_with_tax,
price_without_tax,
)
else:
logger.error("\n ✗ FALLADO: Impuestos no se calculan correctamente")
else:
logger.warning(" ⚠ Producto sin impuestos")
# Test 4: Verificar OCA _get_price
logger.info("\nTEST 4: Verificar OCA _get_price")
logger.info("-" * 60)
pricelist = env["product.pricelist"].search(
[("company_id", "=", env.company.id)], limit=1
)
if pricelist and product:
price_info = product._get_price(
qty=1.0,
pricelist=pricelist,
fposition=False,
)
logger.info(" OCA _get_price:")
logger.info(" value: %.2f", price_info.get("value", 0))
logger.info(
" tax_included: %s", str(price_info.get("tax_included", False))
)
if not price_info.get("tax_included", False):
logger.info(" ✓ PASADO: OCA retorna precio SIN IVA (esperado)")
else:
logger.warning(" ⚠ OCA indica IVA incluido")
logger.info("\n" + "=" * 60)
logger.info("RESUMEN")
logger.info("=" * 60)
logger.info("""
Corrección implementada:
1. Método _compute_price_with_taxes añadido
2. Calcula precio CON IVA usando taxes.compute_all()
3. Usado en eskaera_shop y add_to_eskaera_cart
4. Soluciona problema de precios sin IVA en la tienda
El método OCA _get_price retorna precios SIN IVA.
Nuestra función _compute_price_with_taxes añade el IVA.
""")
logger.info("✓ Todos los tests completados exitosamente\n")
except Exception as e:
logger.exception("\n✗ ERROR: %s\n", e)
import traceback
traceback.print_exc()
sys.exit(1)

View file

@ -1,224 +0,0 @@
#!/bin/bash
# Script para ejecutar tests usando docker run (contenedor aislado)
echo "=========================================="
echo "Ejecutando tests con docker run"
echo "=========================================="
# Verificar que la red de docker-compose existe
docker network inspect addons-cm_default >/dev/null 2>&1 || {
echo "Creando red de docker..."
docker network create addons-cm_default
}
# Ejecutar tests en un contenedor temporal
docker run --rm \
--network addons-cm_default \
-v "$(pwd)":/mnt/extra-addons \
-e HOST=db \
-e PORT=5432 \
-e USER=odoo \
-e PASSWORD=odoo \
odoo:18 \
python3 << 'PYTHON_TEST'
import sys
import os
# Configurar paths
sys.path.insert(0, '/usr/lib/python3/dist-packages')
os.chdir('/mnt/extra-addons')
import odoo
from odoo import api, SUPERUSER_ID
# Configurar Odoo
odoo.tools.config['db_host'] = 'db'
odoo.tools.config['db_port'] = 5432
odoo.tools.config['db_user'] = 'odoo'
odoo.tools.config['db_password'] = 'odoo'
odoo.tools.config['addons_path'] = '/usr/lib/python3/dist-packages/odoo/addons,/mnt/extra-addons'
print("\n=== Conectando a la base de datos ===")
try:
# Conectar a la base de datos
db_name = 'odoo'
registry = odoo.registry(db_name)
with registry.cursor() as cr:
env = api.Environment(cr, SUPERUSER_ID, {})
print("✓ Conectado a la base de datos Odoo")
print(f" Base de datos: {db_name}")
print(f" Usuario: {env.user.name}")
print(f" Compañía: {env.company.name}")
# Test 1: Verificar que el módulo está instalado
print("\n=== Test 1: Verificar módulo website_sale_aplicoop ===")
module = env['ir.module.module'].search([
('name', '=', 'website_sale_aplicoop')
], limit=1)
if module and module.state == 'installed':
print(f"✓ Módulo instalado (versión: {module.installed_version})")
else:
print(f"✗ Módulo NO instalado (estado: {module.state if module else 'no encontrado'})")
# Test 2: Verificar método _compute_price_with_taxes
print("\n=== Test 2: Verificar método _compute_price_with_taxes ===")
try:
from odoo.addons.website_sale_aplicoop.controllers.website_sale import AplicoopWebsiteSale
controller = AplicoopWebsiteSale()
if hasattr(controller, '_compute_price_with_taxes'):
print("✓ Método _compute_price_with_taxes encontrado")
# Verificar firma del método
import inspect
sig = inspect.signature(controller._compute_price_with_taxes)
params = list(sig.parameters.keys())
print(f" Parámetros: {params}")
else:
print("✗ Método _compute_price_with_taxes NO encontrado")
except ImportError as e:
print(f"✗ Error al importar controlador: {e}")
# Test 3: Calcular precio con impuesto 21%
print("\n=== Test 3: Calcular precio con impuesto 21% ===")
# Buscar o crear impuesto 21%
tax_21 = env['account.tax'].search([
('amount', '=', 21.0),
('type_tax_use', '=', 'sale'),
('company_id', '=', env.company.id)
], limit=1)
if not tax_21:
print(" Buscando impuestos existentes...")
all_taxes = env['account.tax'].search([
('type_tax_use', '=', 'sale'),
('company_id', '=', env.company.id)
])
print(f" Impuestos de venta encontrados: {len(all_taxes)}")
for tax in all_taxes[:5]: # Mostrar primeros 5
print(f" - {tax.name}: {tax.amount}%")
# Usar el primer impuesto disponible
if all_taxes:
tax_21 = all_taxes[0]
print(f" Usando impuesto: {tax_21.name} ({tax_21.amount}%)")
else:
print(f" Impuesto encontrado: {tax_21.name} ({tax_21.amount}%)")
if tax_21:
# Buscar un producto existente con impuesto
product = env['product.product'].search([
('taxes_id', 'in', [tax_21.id])
], limit=1)
if not product:
print(" No se encontró producto con este impuesto, buscando cualquier producto...")
product = env['product.product'].search([
('sale_ok', '=', True)
], limit=1)
if product:
print(f" Producto encontrado: {product.name}")
print(f" Precio lista: {product.list_price:.2f} €")
print(f" Impuestos actuales: {[t.name for t in product.taxes_id]}")
else:
print(f" Producto encontrado: {product.name}")
if product:
# Probar cálculo de impuestos
base_price = 100.0
taxes = product.taxes_id.filtered(
lambda t: t.company_id == env.company
)
if taxes:
tax_result = taxes.compute_all(
base_price,
currency=env.company.currency_id,
quantity=1.0,
product=product,
)
price_without_tax = tax_result['total_excluded']
price_with_tax = tax_result['total_included']
tax_amount = price_with_tax - price_without_tax
print(f"\n Cálculo de impuestos:")
print(f" Precio base: {base_price:.2f} €")
print(f" Precio sin impuestos: {price_without_tax:.2f} €")
print(f" Impuestos aplicados: {tax_amount:.2f} €")
print(f" Precio CON impuestos: {price_with_tax:.2f} €")
# Verificar que el precio con impuestos es mayor
if price_with_tax > price_without_tax:
print(f" ✓ Test PASADO: Los impuestos se suman correctamente")
else:
print(f" ✗ Test FALLADO: El precio con impuestos debería ser mayor")
else:
print(" ⚠ Producto sin impuestos configurados")
else:
print(" ✗ No se encontró ningún producto para probar")
else:
print(" ✗ No se encontró ningún impuesto")
# Test 4: Verificar comportamiento de OCA _get_price
print("\n=== Test 4: Verificar OCA _get_price (sin impuestos) ===")
product = env['product.product'].search([
('sale_ok', '=', True)
], limit=1)
if product:
pricelist = env['product.pricelist'].search([
('company_id', '=', env.company.id)
], limit=1)
if pricelist:
try:
price_info = product._get_price(
qty=1.0,
pricelist=pricelist,
fposition=False,
)
print(f" Producto: {product.name}")
print(f" Precio OCA (value): {price_info.get('value', 0):.2f} €")
print(f" Tax included: {price_info.get('tax_included', False)}")
print(f" Original value: {price_info.get('original_value', 0):.2f} €")
print(f" Discount: {price_info.get('discount', 0):.1f}%")
if not price_info.get('tax_included', False):
print(" ✓ OCA retorna precio SIN impuestos incluidos (comportamiento esperado)")
else:
print(" ⚠ OCA indica que los impuestos están incluidos")
except Exception as e:
print(f" ✗ Error al llamar _get_price: {e}")
else:
print(" ✗ No se encontró pricelist")
else:
print(" ✗ No se encontró producto")
print("\n=== Resumen ===")
print("Los cambios implementados:")
print("1. Método _compute_price_with_taxes añadido al controlador")
print("2. Este método calcula el precio CON impuestos incluidos")
print("3. Se usa en eskaera_shop y add_to_eskaera_cart")
print("4. Soluciona el problema de mostrar precios sin IVA")
print()
except Exception as e:
print(f"\n✗ Error general: {e}")
import traceback
traceback.print_exc()
print("\n=== Tests completados ===\n")
PYTHON_TEST
echo ""
echo "Tests finalizados"