addons-cm/CORRECCION_PRECIOS_IVA.md
snt 4d23e98f7b Revertir cambio: eliminar cálculo duplicado de impuestos
El método _get_price() del addon OCA ya maneja correctamente los impuestos
según la configuración de Odoo. El cálculo adicional con compute_all() estaba
duplicando los impuestos cuando price_include estaba activado.

Cambios:
- Eliminado método _compute_price_with_taxes()
- Revertido eskaera_shop() para usar directamente _get_price()
- Revertido add_to_eskaera_cart() para usar directamente _get_price()

El precio mostrado ahora respeta la configuración de impuestos de Odoo
sin duplicación.
2026-02-11 19:54:28 +01:00

175 lines
5.2 KiB
Markdown

# Resumen de Corrección: Precios sin IVA en Tienda Online
## Problema Identificado
La tienda online de Aplicoop mostraba precios **sin impuestos incluidos** cuando debería mostrar precios con IVA.
## Causa Raíz
El método `_get_price()` del addon OCA `product_get_price_helper` retorna el precio base **sin impuestos** por defecto. El campo `tax_included` solo indica si el producto tiene impuestos con `price_include=True`, pero no calcula automáticamente el precio con impuestos añadidos.
## Solución Implementada
### 1. Nuevo Método: `_compute_price_with_taxes()`
**Archivo**: `website_sale_aplicoop/controllers/website_sale.py`
```python
def _compute_price_with_taxes(self, product_variant, base_price, pricelist=None, fposition=None):
"""
Calcula el precio con impuestos incluidos.
Args:
product_variant: product.product recordset
base_price: float - precio base sin impuestos
pricelist: product.pricelist recordset (opcional)
fposition: account.fiscal.position recordset (opcional)
Returns:
float - precio con impuestos incluidos
"""
# 1. Obtener impuestos del producto
taxes = product_variant.taxes_id.filtered(
lambda tax: tax.company_id == request.env.company
)
# 2. Aplicar posición fiscal si existe
if fposition:
taxes = fposition.map_tax(taxes)
# 3. Si no hay impuestos, retornar precio base
if not taxes:
return base_price
# 4. Calcular impuestos usando compute_all()
tax_result = taxes.compute_all(
base_price,
currency=pricelist.currency_id if pricelist else request.env.company.currency_id,
quantity=1.0,
product=product_variant,
)
# 5. Retornar precio CON impuestos incluidos
return tax_result['total_included']
```
### 2. Actualización en `eskaera_shop()`
El método que muestra la lista de productos ahora calcula precios con IVA:
**ANTES:**
```python
price = price_info.get('value', 0.0) # Sin impuestos
product_price_info[product.id] = {
'price': price, # ❌ Sin IVA
'tax_included': price_info.get('tax_included', True),
}
```
**DESPUÉS:**
```python
base_price = price_info.get('value', 0.0) # Precio base sin impuestos
# Calcular precio CON impuestos
price_with_taxes = self._compute_price_with_taxes(
product_variant,
base_price,
pricelist,
request.website.fiscal_position_id
)
product_price_info[product.id] = {
'price': price_with_taxes, # ✓ CON IVA incluido
'tax_included': True, # Ahora siempre True
}
```
### 3. Actualización en `add_to_eskaera_cart()`
El método que añade productos al carrito también calcula con IVA:
**ANTES:**
```python
price_with_tax = price_info.get('value', product.list_price) # ❌ Sin IVA
```
**DESPUÉS:**
```python
base_price = price_info.get('value', product.list_price)
# Calcular precio CON impuestos
price_with_tax = self._compute_price_with_taxes(
product_variant,
base_price,
pricelist,
request.website.fiscal_position_id
) # ✓ CON IVA incluido
```
## Ejemplo Práctico
### Producto con IVA 21%
- **Precio base**: 100.00 €
- **IVA (21%)**: 21.00 €
- **Precio mostrado**: **121.00 €**
### Producto con IVA 10%
- **Precio base**: 100.00 €
- **IVA (10%)**: 10.00 €
- **Precio mostrado**: **110.00 €**
### Producto sin IVA
- **Precio base**: 100.00 €
- **IVA**: 0.00 €
- **Precio mostrado**: **100.00 €**
## Tests Creados
### Archivo: `test_price_with_taxes_included.py`
Contiene tests unitarios que verifican:
1. ✓ Cálculo correcto de IVA 21%
2. ✓ Cálculo correcto de IVA 10%
3. ✓ Productos sin IVA
4. ✓ Múltiples impuestos
5. ✓ Posiciones fiscales
6. ✓ Precios con alta precisión
7. ✓ Comportamiento de OCA `_get_price()`
## Archivos Modificados
1. **`website_sale_aplicoop/controllers/website_sale.py`**
- Añadido método `_compute_price_with_taxes()`
- Actualizado `eskaera_shop()` para usar precios con IVA
- Actualizado `add_to_eskaera_cart()` para usar precios con IVA
2. **`website_sale_aplicoop/tests/test_price_with_taxes_included.py`** (nuevo)
- 13 tests para verificar cálculos de impuestos
## Validación
Para verificar la corrección en producción:
```bash
# 1. Reiniciar Odoo
docker-compose restart odoo
# 2. Actualizar el módulo
docker-compose exec odoo odoo -c /etc/odoo/odoo.conf -d odoo -u website_sale_aplicoop --stop-after-init
# 3. Verificar en navegador
# Ir a: http://localhost:8069/eskaera
# Los precios ahora deberían mostrar IVA incluido
```
## Beneficios
**Transparencia**: Los usuarios ven el precio final que pagarán
**Cumplimiento legal**: Obligatorio mostrar precios con IVA en B2C
**Consistencia**: Todos los precios mostrados incluyen impuestos
**Mantenibilidad**: Código limpio y documentado
**Testeable**: Tests unitarios comprueban el funcionamiento
## Notas Técnicas
- El método utiliza `taxes.compute_all()` de Odoo, que es el estándar para calcular impuestos
- Respeta las posiciones fiscales configuradas
- Compatible con múltiples impuestos por producto
- Maneja correctamente productos sin impuestos
- El precio base (sin IVA) se usa internamente para cálculos de descuentos
- El precio final (con IVA) se muestra al usuario