[FIX] website_sale_aplicoop: Correct XPath for block element

- Changed xpath from div[@id='website_info_settings'] to block[@id='website_info_settings']
- Fixes RPC error when loading res.config.settings view

[FIX] product_price_category_supplier: Convert README to reStructuredText

- Converted README.md to README.rst for proper Odoo documentation
- Fixed docutils warnings and formatting issues
- Updated reStructuredText syntax for code blocks and literals
This commit is contained in:
snt 2026-02-16 15:16:56 +01:00
parent 0d5f0be88c
commit 5ba8ddda92
3 changed files with 410 additions and 44 deletions

View file

@ -49,39 +49,39 @@ Este repositorio contiene addons personalizados y modificados de Odoo 18.0. El p
1. **Estructura de carpeta i18n/**: 1. **Estructura de carpeta i18n/**:
``` ```
addon_name/ addon_name/
├── i18n/ ├── i18n/
│ ├── es.po # Español (obligatorio) │ ├── es.po # Español (obligatorio)
│ ├── eu.po # Euskera (obligatorio) │ ├── eu.po # Euskera (obligatorio)
│ └── addon_name.pot # Template (generado) │ └── addon_name.pot # Template (generado)
``` ```
2. **NO usar `_()` en definiciones de campos a nivel de módulo**: 2. **NO usar `_()` en definiciones de campos a nivel de módulo**:
```python ```python
# ❌ INCORRECTO - causa warnings # ❌ INCORRECTO - causa warnings
from odoo import _ from odoo import _
name = fields.Char(string=_("Name")) name = fields.Char(string=_("Name"))
# ✅ CORRECTO - traducción se maneja por .po files # ✅ CORRECTO - traducción se maneja por .po files
name = fields.Char(string="Name") name = fields.Char(string="Name")
``` ```
3. **Usar `_()` solo en métodos y código ejecutable**: 3. **Usar `_()` solo en métodos y código ejecutable**:
```python ```python
def action_confirm(self): def action_confirm(self):
message = _("Confirmed successfully") message = _("Confirmed successfully")
return {'warning': {'message': message}} return {'warning': {'message': message}}
``` ```
4. **Generar/actualizar traducciones**: 4. **Generar/actualizar traducciones**:
```bash ```bash
# Exportar términos a traducir # Exportar términos a traducir
Pedir al usuario generar a través de UI, no sabemos el método correcto para exportar SÓLO las cadenas del addon sin incluir todo el sistema. Pedir al usuario generar a través de UI, no sabemos el método correcto para exportar SÓLO las cadenas del addon sin incluir todo el sistema.
``` ```
Usar sólo polib y apend cadenas en los archivos .po, msmerge corrompe los archivos. Usar sólo polib y apend cadenas en los archivos .po, msmerge corrompe los archivos.
@ -147,7 +147,7 @@ addons-cm/
### Local Development ### Local Development
```bash ```bash
# Iniciar entorno # Iniciar entorno (puertos: 8070=web, 8073=longpolling)
docker-compose up -d docker-compose up -d
# Actualizar addon # Actualizar addon
@ -158,20 +158,37 @@ docker-compose logs -f odoo
# Ejecutar tests # Ejecutar tests
docker-compose exec odoo odoo -d odoo --test-enable --stop-after-init -u addon_name docker-compose exec odoo odoo -d odoo --test-enable --stop-after-init -u addon_name
# Acceder a shell de Odoo
docker-compose exec odoo bash
# Acceder a PostgreSQL
docker-compose exec db psql -U odoo -d odoo
```` ````
### Quality Checks ### Quality Checks
```bash ```bash
# Ejecutar todos los checks # Ejecutar todos los checks (usa .pre-commit-config.yaml)
pre-commit run --all-files pre-commit run --all-files
# O usar Makefile # O usar Makefile (ver `make help` para todos los comandos)
make lint # Solo linting make lint # Solo linting (pre-commit)
make format # Formatear código make format # Formatear código (black + isort)
make check-addon # Verificar addon específico make check-format # Verificar formateo sin modificar
make flake8 # Ejecutar flake8
make pylint # Ejecutar pylint (todos)
make pylint-required # Solo verificaciones mandatorias
make clean # Limpiar archivos temporales
``` ```
### Tools Configuration
- **black**: Line length 88, target Python 3.10+ (ver `pyproject.toml`)
- **isort**: Profile black, sections: STDLIB > THIRDPARTY > ODOO > ODOO_ADDONS > FIRSTPARTY > LOCALFOLDER
- **flake8**: Ver `.flake8` para reglas específicas
- **pylint**: Configurado para Odoo con `pylint-odoo` plugin
### Testing ### Testing
- Tests en `tests/` de cada addon - Tests en `tests/` de cada addon
@ -179,6 +196,37 @@ make check-addon # Verificar addon específico
- Herencia: `odoo.tests.common.TransactionCase` - Herencia: `odoo.tests.common.TransactionCase`
- Ejecutar: `--test-enable` flag - Ejecutar: `--test-enable` flag
## Critical Architecture Patterns
### Product Variants Architecture
**IMPORTANTE**: Los campos de lógica de negocio SIEMPRE van en `product.product` (variantes), no en `product.template`:
```python
# ✅ CORRECTO - Lógica en product.product
class ProductProduct(models.Model):
_inherit = 'product.product'
last_purchase_price_updated = fields.Boolean(default=False)
list_price_theoritical = fields.Float(default=0.0)
def _compute_theoritical_price(self):
for product in self:
# Cálculo real por variante
pass
# ✅ CORRECTO - Template solo tiene campos related
class ProductTemplate(models.Model):
_inherit = 'product.template'
last_purchase_price_updated = fields.Boolean(
related='product_variant_ids.last_purchase_price_updated',
readonly=False
)
```
**Por qué**: Evita problemas con pricelists y reportes que operan a nivel de variante. Ver `product_sale_price_from_pricelist` como ejemplo.
## Common Patterns ## Common Patterns
### Extending Models ### Extending Models
@ -233,6 +281,35 @@ return {
} }
``` ```
### Logging Pattern
```python
import logging
_logger = logging.getLogger(__name__)
# En métodos de cálculo de precios, usar logging detallado:
_logger.info(
"[PRICE DEBUG] Product %s [%s]: base_price=%.2f, tax_amount=%.2f",
product.default_code or product.name,
product.id,
base_price,
tax_amount,
)
```
### Price Calculation Pattern
```python
# Usar product_get_price_helper para cálculos consistentes
partial_price = product._get_price(qty=1, pricelist=pricelist)
base_price = partial_price.get('value', 0.0) or 0.0
# Siempre validar taxes
if not product.taxes_id:
raise UserError(_("No taxes defined for product %s") % product.name)
```
## Dependencies Management ## Dependencies Management
### OCA Dependencies (`oca_dependencies.txt`) ### OCA Dependencies (`oca_dependencies.txt`)
@ -287,7 +364,22 @@ access_model_user,model.name.user,model_model_name,base.group_user,1,1,1,0
### Price Calculation ### Price Calculation
**Problem**: Prices not updating from pricelist **Problem**: Prices not updating from pricelist
**Solution**: Use `product_sale_price_from_pricelist` with proper configuration **Solution**:
1. Use `product_sale_price_from_pricelist` with proper configuration
2. Set pricelist in Settings > Sales > Automatic Price Configuration
3. Ensure `last_purchase_price_compute_type` is NOT set to `manual_update`
4. Verify product has taxes configured (required for price calculation)
### Product Variant Issues
**Problem**: Computed fields not working in pricelists/reports
**Solution**: Move business logic from `product.template` to `product.product` and use `related` fields in template
### Manifest Dependencies
**Problem**: Module not loading, dependency errors
**Solution**: Check both `__manifest__.py` depends AND `oca_dependencies.txt` for OCA repos
## Testing Guidelines ## Testing Guidelines
@ -307,18 +399,18 @@ access_model_user,model.name.user,model_model_name,base.group_user,1,1,1,0
```javascript ```javascript
odoo.define("module.tour", function (require) { odoo.define("module.tour", function (require) {
"use strict"; "use strict";
var tour = require("web_tour.tour"); var tour = require("web_tour.tour");
tour.register( tour.register(
"tour_name", "tour_name",
{ {
test: true, test: true,
url: "/web", url: "/web",
}, },
[ [
// Tour steps // Tour steps
], ],
); );
}); });
``` ```
@ -364,11 +456,41 @@ Cada addon debe tener un README.md con:
7. **Technical Details**: Modelos, campos, métodos 7. **Technical Details**: Modelos, campos, métodos
8. **Translations**: Estado de traducciones (si aplica) 8. **Translations**: Estado de traducciones (si aplica)
### **manifest**.py Structure
Todos los addons custom deben seguir esta estructura:
```python
# Copyright YEAR - Today AUTHOR
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{ # noqa: B018
"name": "Addon Name",
"version": "18.0.X.Y.Z", # X=major, Y=minor, Z=patch
"category": "category_name",
"summary": "Short description",
"author": "Odoo Community Association (OCA), Your Company",
"maintainers": ["maintainer_github"],
"website": "https://github.com/OCA/repo",
"license": "AGPL-3",
"depends": [
"base",
# Lista ordenada alfabéticamente
],
"data": [
"security/ir.model.access.csv",
"views/actions.xml",
"views/menu.xml",
"views/model_views.xml",
],
}
```
### Code Comments ### Code Comments
- Docstrings en clases y métodos públicos - Docstrings en clases y métodos públicos
- Comentarios inline para lógica compleja - Comentarios inline para lógica compleja
- TODOs con contexto completo - TODOs con contexto completo
- Logging detallado en operaciones de precios/descuentos
## Version Control ## Version Control
@ -397,6 +519,61 @@ Tags: `[ADD]`, `[FIX]`, `[IMP]`, `[REF]`, `[REM]`, `[I18N]`, `[DOC]`
- Indexes en campos frecuentemente buscados - Indexes en campos frecuentemente buscados
- Avoid N+1 queries con `prefetch` - Avoid N+1 queries con `prefetch`
## Key Business Features
### Eskaera System (website_sale_aplicoop)
Sistema completo de compras colaborativas para cooperativas de consumo:
- **Group Orders**: Pedidos grupales con estados (draft → confirmed → collected → completed)
- **Separate Carts**: Carrito independiente por miembro y por grupo
- **Cutoff Dates**: Validación de fechas límite para pedidos
- **Pickup Management**: Gestión de días de recogida
- **Multi-language**: ES, EU, CA, GL, PT, FR, IT
- **Member Tracking**: Gestión de miembros activos/inactivos por grupo
**Flujo típico**:
1. Administrador crea grupo order con fechas (collection, cutoff, pickup)
2. Miembros añaden productos a su carrito individual
3. Sistema valida cutoff date antes de confirmar
4. Notificaciones automáticas al cambiar estados
5. Tracking de fulfillment por miembro
Ver [website_sale_aplicoop/README.md](../website_sale_aplicoop/README.md) para detalles.
### Triple Discount System
Todos los documentos de compra/venta soportan 3 descuentos consecutivos:
```python
# Ejemplo: Precio = 600.00
# Desc. 1 = 50% → 300.00
# Desc. 2 = 50% → 150.00
# Desc. 3 = 50% → 75.00
```
**IMPORTANTE**: Usar `account_invoice_triple_discount_readonly` para evitar bug de acumulación de descuentos.
### Automatic Pricing System
`product_sale_price_from_pricelist` calcula automáticamente precio de venta basado en:
- Último precio de compra (`last_purchase_price_received`)
- Tipo de cálculo de descuentos (`last_purchase_price_compute_type`)
- Pricelist configurado en Settings
- Impuestos del producto
**Configuración crítica**:
```python
# En Settings > Sales > Automatic Price Configuration
product_pricelist_automatic = [ID_pricelist]
# En producto
last_purchase_price_compute_type != "manual_update" # Para auto-cálculo
```
## Resources ## Resources
- **OCA Guidelines**: https://github.com/OCA/odoo-community.org/blob/master/website/Contribution/CONTRIBUTING.rst - **OCA Guidelines**: https://github.com/OCA/odoo-community.org/blob/master/website/Contribution/CONTRIBUTING.rst
@ -406,6 +583,6 @@ Tags: `[ADD]`, `[FIX]`, `[IMP]`, `[REF]`, `[REM]`, `[I18N]`, `[DOC]`
--- ---
**Last Updated**: 2026-02-12 **Last Updated**: 2026-02-16
**Odoo Version**: 18.0 **Odoo Version**: 18.0
**Python Version**: 3.10+ **Python Version**: 3.10+

View file

@ -0,0 +1,189 @@
======================================
Product Price Category - Supplier
======================================
Extiende ``res.partner`` (proveedores) con un campo de categoría de precio por
defecto y permite actualizar masivamente todos los productos de un proveedor con
esta categoría mediante un wizard.
Funcionalidades
===============
- **Campo en Proveedores**: Añade campo ``default_price_category_id`` en la
pestaña "Compras" (Purchases) de res.partner
- **Actualización Masiva**: Botón que abre wizard modal para confirmar
actualización de todos los productos del proveedor
- **Columna Configurable**: Campo oculto en vista tree de partner,
visible/configurable desde menú de columnas
- **Control de Permisos**: Acceso restringido a
``sales_team.group_sale_manager`` (Gestores de Ventas)
Dependencias
============
- ``product_price_category`` (OCA addon base)
- ``product_pricelists_margins_custom`` (Addon del proyecto)
- ``sales_team`` (Odoo core)
Instalación
===========
.. code-block:: bash
docker-compose exec -T odoo odoo -d odoo -u product_price_category_supplier --stop-after-init
Flujo de Uso
============
1. Abrir formulario de un **Proveedor** (res.partner)
2. Ir a pestaña **"Compras"** (Purchases)
3. En sección **"Price Category Settings"**, seleccionar **categoría de precio
por defecto**
4. Hacer clic en botón **"Apply to All Products"**
5. Se abre modal de confirmación mostrando:
- Nombre del proveedor
- Categoría de precio a aplicar
- Cantidad de productos que serán actualizados
6. Hacer clic **"Confirm"** para ejecutar actualización en bulk
7. Notificación de éxito mostrando cantidad de productos actualizados
Campos
======
res.partner
-----------
- ``default_price_category_id`` (Many2one → product.price.category)
- Ubicación: Pestaña "Compras", sección "Price Category Settings"
- Obligatorio: No
- Ayuda: "Default price category for products from this supplier"
- Visible en tree: Oculto por defecto (column_invisible=1), configurable vía menú
Modelos
=======
wizard.update.product.category (Transient)
-------------------------------------------
- ``partner_id`` (Many2one → res.partner) - Readonly
- ``partner_name`` (Char, related to partner_id.name) - Readonly
- ``price_category_id`` (Many2one → product.price.category) - Readonly
- ``product_count`` (Integer) - Cantidad de productos a actualizar - Readonly
**Métodos**:
- ``action_confirm()`` - Realiza bulk update de productos y retorna notificación
Vistas
======
res.partner
-----------
- **Form**: Campo + botón en pestaña "Compras"
- **Tree**: Campo oculto (column_invisible=1)
wizard.update.product.category
------------------------------
- **Form**: Formulario modal con información de confirmación y botones
Seguridad
=========
Acceso al wizard restringido a grupo ``sales_team.group_sale_manager``:
- Lectura: Sí
- Escritura: Sí
- Creación: Sí
- Borrado: Sí
Comportamiento
==============
Actualización de Productos
--------------------------
Cuando el usuario confirma la acción:
1. Se buscan todos los productos (``product.template``) donde:
- ``default_supplier_id = partner_id`` (este proveedor es su proveedor por
defecto)
2. Se actualizan en bulk (single SQL UPDATE) con:
- ``price_category_id = default_price_category_id``
3. Se retorna notificación de éxito:
- "X products updated with category 'CATEGORY_NAME'."
**Nota**: La actualización SOBRESCRIBE cualquier ``price_category_id``
existente en los productos.
Extensión Futura
================
Para implementar defaults automáticos al crear productos desde un proveedor:
.. code-block:: python
# En models/product_template.py
@api.model_create_multi
def create(self, vals_list):
# Si se proporciona default_supplier_id sin price_category_id,
# usar default_price_category_id del proveedor
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)
Traducciones
============
Para añadir/actualizar traducciones:
.. code-block:: bash
# Exportar strings
docker-compose exec -T odoo odoo -d odoo \
--addons-path=/mnt/extra-addons/product_price_category_supplier \
-i product_price_category_supplier \
--i18n-export=/tmp/product_price_category_supplier.pot \
--stop-after-init
# Mergar en archivos .po existentes
cd product_price_category_supplier/i18n
for lang in es eu; do
msgmerge -U ${lang}.po product_price_category_supplier.pot
done
Testing
=======
Ejecutar tests:
.. code-block:: bash
docker-compose exec -T odoo odoo -d odoo \
-i product_price_category_supplier \
--test-enable --stop-after-init
Créditos
========
Autor
-----
Your Company - 2026
Licencia
--------
AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)

View file

@ -5,7 +5,7 @@
<field name="model">res.config.settings</field> <field name="model">res.config.settings</field>
<field name="inherit_id" ref="website.res_config_settings_view_form"/> <field name="inherit_id" ref="website.res_config_settings_view_form"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="//div[@id='website_info_settings']" position="after"> <xpath expr="//block[@id='website_info_settings']" position="after">
<h2>Aplicoop Settings</h2> <h2>Aplicoop Settings</h2>
<div class="row mt16 o_settings_container" id="aplicoop_settings"> <div class="row mt16 o_settings_container" id="aplicoop_settings">
<div class="col-12 col-lg-6 o_setting_box"> <div class="col-12 col-lg-6 o_setting_box">