[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.
This commit is contained in:
parent
2a480b74bb
commit
b10ba1fc15
12 changed files with 1327 additions and 0 deletions
175
docs/CORRECCION_PRECIOS_IVA.md
Normal file
175
docs/CORRECCION_PRECIOS_IVA.md
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
# 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue