addons-cm/docs/CORRECCION_PRECIOS_IVA.md
snt b10ba1fc15 [DOC] all: Reorganize and consolidate project documentation
- Create .github/copilot-instructions.md with global development guidelines
- Add comprehensive README.md at project root with quick start guide
- Create docs/ directory for technical documentation
- Move installation and linter docs to docs/
- Add docs/TRANSLATIONS.md with complete translation system guide
- Create README.md for OCA modified addons (product_origin, product_get_price_helper, product_main_seller)
- Document translation best practices (no _() in field definitions)
- Add references between all documentation files
- Clean up project root by moving technical docs to docs/

All documentation now properly references addon-specific READMEs for detailed architecture and implementation.
2026-02-12 16:25:49 +01:00

5.2 KiB

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

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:

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:

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:

price_with_tax = price_info.get('value', product.list_price)  # ❌ Sin IVA

DESPUÉS:

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:

# 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