[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
|
||||
215
docs/INSTALACION_COMPLETA.md
Normal file
215
docs/INSTALACION_COMPLETA.md
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
# ✅ Instalación Completa - Herramientas de Verificación OCA
|
||||
|
||||
## Resumen de la Instalación
|
||||
|
||||
Se han instalado y configurado exitosamente todas las herramientas de verificación OCA estándar en este repositorio.
|
||||
|
||||
## 📦 Paquetes Instalados
|
||||
|
||||
Los siguientes paquetes Python han sido instalados en el entorno virtual (`.venv`):
|
||||
|
||||
- ✅ **pre-commit** - Gestor de hooks de git
|
||||
- ✅ **black** - Formateador de código Python
|
||||
- ✅ **isort** - Ordenador de imports
|
||||
- ✅ **flake8** - Linter de estilo
|
||||
- ✅ **flake8-bugbear** - Plugin adicional para flake8
|
||||
- ✅ **pylint** - Analizador estático de código
|
||||
- ✅ **pylint-odoo** - Plugin específico para Odoo
|
||||
- ✅ **autoflake** - Elimina imports y variables no usadas
|
||||
- ✅ **pyupgrade** - Actualiza sintaxis de Python
|
||||
|
||||
## 📄 Archivos de Configuración Creados
|
||||
|
||||
### Configuración Principal
|
||||
- ✅ `.pre-commit-config.yaml` - Configuración de pre-commit con hooks OCA
|
||||
- ✅ `.pylintrc` - Reglas de pylint (opcionales + mandatorias)
|
||||
- ✅ `.pylintrc-mandatory` - Solo reglas mandatorias de pylint
|
||||
- ✅ `.flake8` - Configuración de flake8
|
||||
- ✅ `.isort.cfg` - Configuración de isort
|
||||
- ✅ `setup.cfg` - Configuración compartida
|
||||
- ✅ `pyproject.toml` - Configuración de black e isort
|
||||
- ✅ `.editorconfig` - Configuración de editor
|
||||
- ✅ `requirements.txt` - Dependencias del proyecto
|
||||
|
||||
### Herramientas y Scripts
|
||||
- ✅ `Makefile` - Comandos make para tareas comunes
|
||||
- ✅ `check_addon.sh` - Script de verificación rápida de addons
|
||||
- ✅ `LINTERS_README.md` - Documentación de uso
|
||||
|
||||
### VS Code
|
||||
- ✅ `.vscode/settings.json` - Configuración actualizada
|
||||
- ✅ `.vscode/extensions.json` - Extensiones recomendadas
|
||||
|
||||
## 🔧 Hooks de Pre-commit Instalados
|
||||
|
||||
Los siguientes hooks se ejecutarán automáticamente en cada commit:
|
||||
|
||||
1. **forbidden-files** - Detecta archivos prohibidos (.rej)
|
||||
2. **oca-update-pre-commit-excluded-addons** - Actualiza lista de addons excluidos
|
||||
3. **autoflake** - Elimina imports/variables no usadas
|
||||
4. **black** - Formatea código Python
|
||||
5. **prettier** - Formatea XML, YAML, JSON
|
||||
6. **eslint** - Verifica JavaScript
|
||||
7. **trailing-whitespace** - Elimina espacios al final de línea
|
||||
8. **end-of-file-fixer** - Asegura nueva línea al final de archivo
|
||||
9. **debug-statements** - Detecta statements de debug
|
||||
10. **fix-encoding-pragma** - Remueve pragmas de encoding
|
||||
11. **check-case-conflict** - Detecta conflictos de mayúsculas/minúsculas
|
||||
12. **check-docstring-first** - Verifica posición de docstrings
|
||||
13. **check-executables-have-shebangs** - Verifica shebangs
|
||||
14. **check-merge-conflict** - Detecta marcadores de conflicto
|
||||
15. **check-symlinks** - Verifica enlaces simbólicos
|
||||
16. **check-xml** - Verifica sintaxis XML
|
||||
17. **mixed-line-ending** - Normaliza finales de línea
|
||||
18. **pyupgrade** - Actualiza sintaxis Python
|
||||
19. **isort** - Ordena imports (excepto __init__.py)
|
||||
20. **setuptools-odoo-make-default** - Genera setup/
|
||||
21. **setuptools-odoo-get-requirements** - Actualiza requirements.txt
|
||||
22. **flake8** - Verifica estilo Python
|
||||
23. **mypy** - Verificación de tipos estática
|
||||
24. **pylint** (opcional) - Análisis de código con exit-zero
|
||||
25. **pylint** (mandatorio) - Análisis de código que falla el commit
|
||||
|
||||
## 🚀 Uso Rápido
|
||||
|
||||
### Comandos Make
|
||||
|
||||
```bash
|
||||
# Ver todos los comandos disponibles
|
||||
make help
|
||||
|
||||
# Ejecutar todas las verificaciones
|
||||
make lint
|
||||
|
||||
# Formatear código
|
||||
make format
|
||||
|
||||
# Verificar formato sin modificar
|
||||
make check-format
|
||||
|
||||
# Ejecutar flake8
|
||||
make flake8
|
||||
|
||||
# Ejecutar pylint en todos los addons
|
||||
make pylint
|
||||
|
||||
# Solo verificaciones mandatorias
|
||||
make pylint-required
|
||||
|
||||
# Limpiar archivos temporales
|
||||
make clean
|
||||
|
||||
# Actualizar requirements.txt
|
||||
make requirements
|
||||
|
||||
# Actualizar hooks a últimas versiones
|
||||
make update-hooks
|
||||
```
|
||||
|
||||
### Script de Verificación de Addon
|
||||
|
||||
```bash
|
||||
# Verificar un addon específico
|
||||
./check_addon.sh account_invoice_triple_discount
|
||||
```
|
||||
|
||||
### Pre-commit Manual
|
||||
|
||||
```bash
|
||||
# Ejecutar en todos los archivos
|
||||
pre-commit run --all-files
|
||||
|
||||
# Ejecutar en archivos modificados
|
||||
pre-commit run
|
||||
|
||||
# Ejecutar un hook específico
|
||||
pre-commit run black --all-files
|
||||
|
||||
# Ejecutar en archivos específicos
|
||||
pre-commit run --files path/to/file.py
|
||||
```
|
||||
|
||||
### Herramientas Individuales
|
||||
|
||||
```bash
|
||||
# Black
|
||||
black .
|
||||
black --check . # Solo verificar sin modificar
|
||||
|
||||
# isort
|
||||
isort .
|
||||
isort --check-only . # Solo verificar sin modificar
|
||||
|
||||
# flake8
|
||||
flake8 .
|
||||
flake8 account_invoice_triple_discount/
|
||||
|
||||
# pylint
|
||||
pylint --rcfile=.pylintrc account_invoice_triple_discount/
|
||||
pylint --rcfile=.pylintrc-mandatory account_invoice_triple_discount/
|
||||
```
|
||||
|
||||
## ⚙️ Configuración de VS Code
|
||||
|
||||
Se recomienda instalar las siguientes extensiones:
|
||||
- Python
|
||||
- Black Formatter
|
||||
- isort
|
||||
- Pylint
|
||||
- Flake8
|
||||
- Prettier
|
||||
- EditorConfig
|
||||
- Odoo Snippets
|
||||
|
||||
Las extensiones recomendadas están definidas en `.vscode/extensions.json`.
|
||||
|
||||
## 🔍 Verificación de la Instalación
|
||||
|
||||
Para verificar que todo está funcionando correctamente:
|
||||
|
||||
```bash
|
||||
# 1. Verificar versiones instaladas
|
||||
black --version
|
||||
flake8 --version
|
||||
pylint --version
|
||||
isort --version
|
||||
pre-commit --version
|
||||
|
||||
# 2. Probar black en un archivo
|
||||
black --check account_invoice_triple_discount/models/
|
||||
|
||||
# 3. Probar flake8 en un addon
|
||||
flake8 account_invoice_triple_discount/
|
||||
|
||||
# 4. Ejecutar un hook simple
|
||||
pre-commit run forbidden-files --all-files
|
||||
```
|
||||
|
||||
## 📚 Documentación Adicional
|
||||
|
||||
- Ver [LINTERS_README.md](LINTERS_README.md) para más detalles
|
||||
- [OCA Guidelines](https://github.com/OCA/maintainer-tools)
|
||||
- [pre-commit](https://pre-commit.com/)
|
||||
- [pylint-odoo](https://github.com/OCA/pylint-odoo)
|
||||
- [Black](https://black.readthedocs.io/)
|
||||
|
||||
## 🎯 Próximos Pasos
|
||||
|
||||
1. Ejecutar `make lint` para verificar el estado actual del código
|
||||
2. Corregir cualquier problema encontrado
|
||||
3. Hacer commit con los cambios (pre-commit se ejecutará automáticamente)
|
||||
4. Si hay problemas que no se pueden resolver, revisar la configuración específica en los archivos `.pylintrc`, `.flake8`, etc.
|
||||
|
||||
## ⚠️ Notas Importantes
|
||||
|
||||
- Los hooks de pre-commit se ejecutarán automáticamente en cada commit
|
||||
- Si necesitas hacer un commit sin verificación (NO RECOMENDADO): `git commit --no-verify`
|
||||
- Los archivos `__init__.py` son excluidos de la ordenación de imports por convención OCA
|
||||
- Las verificaciones de pylint tienen dos niveles: opcionales (con `--exit-zero`) y mandatorias (que fallan el commit)
|
||||
- El formateo con black y prettier puede modificar archivos automáticamente
|
||||
|
||||
---
|
||||
|
||||
**✅ Instalación completada exitosamente**
|
||||
|
||||
Fecha: 11 de febrero de 2026
|
||||
88
docs/LINTERS_README.md
Normal file
88
docs/LINTERS_README.md
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
# Herramientas de Verificación OCA
|
||||
|
||||
Este repositorio está configurado con las herramientas de verificación estándar de OCA (Odoo Community Association).
|
||||
|
||||
## Herramientas Instaladas
|
||||
|
||||
- **pre-commit**: Gestor de hooks de git para ejecutar verificaciones automáticas
|
||||
- **black**: Formateador de código Python
|
||||
- **isort**: Ordenador de imports Python
|
||||
- **flake8**: Linter de estilo Python (con flake8-bugbear)
|
||||
- **pylint**: Analizador de código Python con pylint-odoo
|
||||
- **autoflake**: Elimina imports y variables no utilizadas
|
||||
- **pyupgrade**: Actualiza sintaxis de Python a versiones más modernas
|
||||
- **prettier**: Formateador para XML, YAML, JSON, etc.
|
||||
|
||||
## Uso
|
||||
|
||||
### Verificación Manual
|
||||
|
||||
Para ejecutar las verificaciones en todos los archivos:
|
||||
|
||||
```bash
|
||||
pre-commit run --all-files
|
||||
```
|
||||
|
||||
Para ejecutar en archivos específicos:
|
||||
|
||||
```bash
|
||||
pre-commit run --files path/to/file.py
|
||||
```
|
||||
|
||||
### Verificación Automática
|
||||
|
||||
Los hooks de pre-commit están configurados para ejecutarse automáticamente en cada commit. Si hay errores, el commit será rechazado hasta que se corrijan.
|
||||
|
||||
### Ejecutar Herramientas Individuales
|
||||
|
||||
```bash
|
||||
# Black
|
||||
black .
|
||||
|
||||
# isort
|
||||
isort .
|
||||
|
||||
# flake8
|
||||
flake8 .
|
||||
|
||||
# pylint (opcional)
|
||||
pylint --rcfile=.pylintrc <addon_name>
|
||||
|
||||
# pylint (mandatorio)
|
||||
pylint --rcfile=.pylintrc-mandatory <addon_name>
|
||||
```
|
||||
|
||||
## Configuración
|
||||
|
||||
Los archivos de configuración son:
|
||||
|
||||
- `.pre-commit-config.yaml`: Configuración de pre-commit hooks
|
||||
- `.pylintrc`: Verificaciones opcionales y mandatorias de pylint
|
||||
- `.pylintrc-mandatory`: Solo verificaciones mandatorias
|
||||
- `.flake8`: Configuración de flake8
|
||||
- `.isort.cfg`: Configuración de isort
|
||||
- `setup.cfg`: Configuración adicional para flake8 e isort
|
||||
- `pyproject.toml`: Configuración de black e isort
|
||||
- `.editorconfig`: Configuración de editor para consistencia
|
||||
|
||||
## Actualización de Hooks
|
||||
|
||||
Para actualizar los hooks a las últimas versiones:
|
||||
|
||||
```bash
|
||||
pre-commit autoupdate
|
||||
```
|
||||
|
||||
## Desactivar Temporalmente
|
||||
|
||||
Si necesitas hacer un commit sin ejecutar las verificaciones (NO RECOMENDADO):
|
||||
|
||||
```bash
|
||||
git commit --no-verify
|
||||
```
|
||||
|
||||
## Más Información
|
||||
|
||||
- [OCA Guidelines](https://github.com/OCA/maintainer-tools)
|
||||
- [pre-commit](https://pre-commit.com/)
|
||||
- [pylint-odoo](https://github.com/OCA/pylint-odoo)
|
||||
42
docs/README.md
Normal file
42
docs/README.md
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# Documentación Técnica
|
||||
|
||||
Esta carpeta contiene documentación técnica y de referencia del proyecto.
|
||||
|
||||
## Contenido
|
||||
|
||||
### Configuración y Desarrollo
|
||||
|
||||
- **[LINTERS_README.md](LINTERS_README.md)** - Guía de herramientas de calidad de código (black, isort, flake8, pylint)
|
||||
- **[TRANSLATIONS.md](TRANSLATIONS.md)** - Guía completa del sistema de traducciones (IMPORTANTE)
|
||||
- **[INSTALACION_COMPLETA.md](INSTALACION_COMPLETA.md)** - Documentación detallada del proceso de instalación
|
||||
- **[RESUMEN_INSTALACION.md](RESUMEN_INSTALACION.md)** - Resumen ejecutivo de la instalación
|
||||
|
||||
### Resolución de Problemas
|
||||
|
||||
- **[CORRECCION_PRECIOS_IVA.md](CORRECCION_PRECIOS_IVA.md)** - Correcciones relacionadas con precios e IVA
|
||||
- **[TEST_MANUAL.md](TEST_MANUAL.md)** - Guía de tests manuales
|
||||
|
||||
## Documentación Principal
|
||||
|
||||
Para información general del proyecto y cómo empezar, ver:
|
||||
|
||||
- **[README.md](../README.md)** - Documentación principal del repositorio
|
||||
- **[.github/copilot-instructions.md](../.github/copilot-instructions.md)** - Instrucciones para GitHub Copilot
|
||||
|
||||
## Documentación por Addon
|
||||
|
||||
Cada addon tiene su propio README:
|
||||
|
||||
### Addons Custom
|
||||
- [account_invoice_triple_discount_readonly](../account_invoice_triple_discount_readonly/README.md)
|
||||
- [product_price_category_supplier](../product_price_category_supplier/README.md)
|
||||
- [product_sale_price_from_pricelist](../product_sale_price_from_pricelist/README.md)
|
||||
- [website_sale_aplicoop](../website_sale_aplicoop/README.md)
|
||||
|
||||
### Addons OCA Modificados
|
||||
- [account_invoice_triple_discount](../account_invoice_triple_discount/README.rst)
|
||||
- [purchase_triple_discount](../purchase_triple_discount/README.rst)
|
||||
- [product_origin](../product_origin/README.md)
|
||||
- [product_get_price_helper](../product_get_price_helper/README.md)
|
||||
- [product_main_seller](../product_main_seller/README.md)
|
||||
- [product_price_category](../product_price_category/README.rst)
|
||||
230
docs/RESUMEN_INSTALACION.md
Normal file
230
docs/RESUMEN_INSTALACION.md
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
# 🎉 Instalación Completada - Linters y Pre-commit OCA
|
||||
|
||||
## ✅ Resumen de la Instalación
|
||||
|
||||
Se han instalado y configurado exitosamente **todas las herramientas de verificación OCA** en el repositorio.
|
||||
|
||||
---
|
||||
|
||||
## 📦 Paquetes Instalados (Versiones)
|
||||
|
||||
| Herramienta | Versión | Descripción |
|
||||
|-------------|---------|-------------|
|
||||
| **black** | 26.1.0 | Formateador de código Python |
|
||||
| **isort** | 7.0.0 | Ordenador de imports |
|
||||
| **flake8** | 7.3.0 | Linter de estilo Python |
|
||||
| **pylint** | 4.0.4 | Analizador estático de código |
|
||||
| **pylint-odoo** | 10.0.0 | Plugin específico para Odoo |
|
||||
| **pre-commit** | ✅ | Gestor de hooks de git |
|
||||
| **autoflake** | ✅ | Elimina código no usado |
|
||||
| **pyupgrade** | ✅ | Actualiza sintaxis Python |
|
||||
| **flake8-bugbear** | ✅ | Verificaciones adicionales |
|
||||
|
||||
---
|
||||
|
||||
## 📁 Archivos Creados
|
||||
|
||||
### Configuración de Linters
|
||||
- ✅ `.pre-commit-config.yaml` - 25 hooks configurados
|
||||
- ✅ `.pylintrc` - Reglas opcionales + mandatorias
|
||||
- ✅ `.pylintrc-mandatory` - Solo reglas obligatorias
|
||||
- ✅ `.flake8` - Configuración de flake8
|
||||
- ✅ `.isort.cfg` - Configuración de isort
|
||||
- ✅ `setup.cfg` - Configuración compartida
|
||||
- ✅ `pyproject.toml` - Configuración de black
|
||||
- ✅ `.editorconfig` - Consistencia de editor
|
||||
|
||||
### Herramientas de Desarrollo
|
||||
- ✅ `Makefile` - 10+ comandos útiles
|
||||
- ✅ `check_addon.sh` - Script de verificación de addons
|
||||
- ✅ `requirements.txt` - Dependencias del proyecto
|
||||
|
||||
### Documentación
|
||||
- ✅ `LINTERS_README.md` - Guía completa de uso
|
||||
- ✅ `INSTALACION_COMPLETA.md` - Documentación detallada
|
||||
- ✅ `RESUMEN_INSTALACION.md` - Este archivo
|
||||
|
||||
### VS Code
|
||||
- ✅ `.vscode/settings.json` - Configuración actualizada
|
||||
- ✅ `.vscode/extensions.json` - Extensiones recomendadas
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Estado de Pre-commit
|
||||
|
||||
```
|
||||
✅ Pre-commit hooks instalados en .git/hooks/
|
||||
✅ 25 hooks configurados y listos
|
||||
✅ Hooks actualizados a las últimas versiones
|
||||
```
|
||||
|
||||
Los hooks se ejecutarán automáticamente en cada commit.
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Comandos Rápidos
|
||||
|
||||
### Comandos más usados
|
||||
|
||||
```bash
|
||||
# Verificar todo el código
|
||||
make lint
|
||||
|
||||
# Formatear código automáticamente
|
||||
make format
|
||||
|
||||
# Verificar un addon específico
|
||||
./check_addon.sh account_invoice_triple_discount
|
||||
|
||||
# Ver todos los comandos disponibles
|
||||
make help
|
||||
```
|
||||
|
||||
### Pre-commit
|
||||
|
||||
```bash
|
||||
# Ejecutar en todos los archivos
|
||||
pre-commit run --all-files
|
||||
|
||||
# Ejecutar en archivos modificados
|
||||
pre-commit run
|
||||
|
||||
# Actualizar a últimas versiones
|
||||
pre-commit autoupdate
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Hooks Configurados (25)
|
||||
|
||||
### Verificaciones Básicas (7)
|
||||
1. `forbidden-files` - Detecta archivos .rej
|
||||
2. `oca-update-pre-commit-excluded-addons` - Actualiza exclusiones
|
||||
3. `trailing-whitespace` - Elimina espacios finales
|
||||
4. `end-of-file-fixer` - Nueva línea al final
|
||||
5. `check-merge-conflict` - Detecta conflictos
|
||||
6. `check-xml` - Valida XML
|
||||
7. `mixed-line-ending` - Normaliza finales de línea
|
||||
|
||||
### Formateo de Código (3)
|
||||
8. `black` - Formatea Python
|
||||
9. `isort` - Ordena imports
|
||||
10. `prettier` - Formatea XML/YAML/JSON
|
||||
|
||||
### Análisis de Código (5)
|
||||
11. `flake8` - Linter Python
|
||||
12. `pylint` (opcional) - Análisis con exit-zero
|
||||
13. `pylint` (mandatorio) - Análisis que falla commit
|
||||
14. `mypy` - Verificación de tipos
|
||||
15. `eslint` - Linter JavaScript
|
||||
|
||||
### Mejoras Automáticas (3)
|
||||
16. `autoflake` - Elimina imports no usados
|
||||
17. `pyupgrade` - Actualiza sintaxis Python (py38+)
|
||||
18. `fix-encoding-pragma` - Remueve pragmas encoding
|
||||
|
||||
### Verificaciones de Seguridad (4)
|
||||
19. `debug-statements` - Detecta debugs olvidados
|
||||
20. `check-executables-have-shebangs` - Verifica shebangs
|
||||
21. `check-symlinks` - Valida enlaces simbólicos
|
||||
22. `check-case-conflict` - Conflictos mayús/minús
|
||||
|
||||
### Odoo Específico (3)
|
||||
23. `setuptools-odoo-make-default` - Genera setup/
|
||||
24. `setuptools-odoo-get-requirements` - Actualiza requirements.txt
|
||||
25. `check-docstring-first` - Verifica docstrings
|
||||
|
||||
---
|
||||
|
||||
## ✅ Verificación de la Instalación
|
||||
|
||||
Ejecuta estos comandos para verificar que todo funciona:
|
||||
|
||||
```bash
|
||||
# 1. Verificar versiones
|
||||
black --version # 26.1.0
|
||||
isort --version # 7.0.0
|
||||
pylint --version # 4.0.4
|
||||
flake8 --version # 7.3.0
|
||||
|
||||
# 2. Probar en un archivo
|
||||
black --check account_invoice_triple_discount/models/account_move_line.py
|
||||
|
||||
# 3. Ejecutar un hook
|
||||
pre-commit run forbidden-files --all-files
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Próximos Pasos
|
||||
|
||||
1. **Ejecutar verificación inicial**
|
||||
```bash
|
||||
make lint
|
||||
```
|
||||
|
||||
2. **Corregir problemas encontrados**
|
||||
```bash
|
||||
make format # Formatea automáticamente
|
||||
```
|
||||
|
||||
3. **Hacer commit**
|
||||
```bash
|
||||
git add .
|
||||
git commit -m "feat: configurar herramientas de verificación OCA"
|
||||
# Pre-commit se ejecutará automáticamente
|
||||
```
|
||||
|
||||
4. **Verificar addons individuales**
|
||||
```bash
|
||||
./check_addon.sh nombre_addon
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 Documentación
|
||||
|
||||
- **Guía completa**: Ver [LINTERS_README.md](LINTERS_README.md)
|
||||
- **Detalles técnicos**: Ver [INSTALACION_COMPLETA.md](INSTALACION_COMPLETA.md)
|
||||
- **OCA Guidelines**: https://github.com/OCA/maintainer-tools
|
||||
- **pre-commit**: https://pre-commit.com/
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ Configuración de VS Code
|
||||
|
||||
Instala las extensiones recomendadas:
|
||||
1. Abre VS Code
|
||||
2. Ve a Extensions (Ctrl+Shift+X)
|
||||
3. Busca "@recommended"
|
||||
4. Instala todas las extensiones sugeridas
|
||||
|
||||
O ejecuta:
|
||||
```bash
|
||||
code --install-extension ms-python.python
|
||||
code --install-extension ms-python.black-formatter
|
||||
code --install-extension ms-python.isort
|
||||
code --install-extension ms-python.pylint
|
||||
code --install-extension ms-python.flake8
|
||||
code --install-extension esbenp.prettier-vscode
|
||||
code --install-extension editorconfig.editorconfig
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Notas Importantes
|
||||
|
||||
- ✅ Los hooks se ejecutan **automáticamente** en cada commit
|
||||
- ✅ Black y prettier **modifican archivos** automáticamente
|
||||
- ✅ Los `__init__.py` están **excluidos** de isort (estándar OCA)
|
||||
- ✅ Pylint tiene **dos niveles**: opcional (no falla) y mandatorio (falla commit)
|
||||
- ⚠️ Para commit sin verificación (NO RECOMENDADO): `git commit --no-verify`
|
||||
|
||||
---
|
||||
|
||||
## 🎊 ¡Todo Listo!
|
||||
|
||||
El repositorio está ahora configurado con los estándares de calidad OCA. Cada commit será verificado automáticamente para mantener la calidad del código.
|
||||
|
||||
**Fecha de instalación**: 11 de febrero de 2026
|
||||
**Versión de configuración**: OCA Standard (2026)
|
||||
74
docs/TEST_MANUAL.md
Normal file
74
docs/TEST_MANUAL.md
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
# Test Manual - Verificar Precios con IVA
|
||||
|
||||
## Para verificar que la corrección funciona:
|
||||
|
||||
### 1. Reiniciar Odoo y actualizar el módulo
|
||||
|
||||
```bash
|
||||
cd /home/snt/Documentos/lab/odoo/kidekoop/addons-cm
|
||||
|
||||
# Actualizar el módulo
|
||||
docker-compose restart odoo
|
||||
docker-compose exec odoo odoo -c /etc/odoo/odoo.conf -d odoo -u website_sale_aplicoop --stop-after-init
|
||||
docker-compose restart odoo
|
||||
```
|
||||
|
||||
### 2. Verificar en el navegador
|
||||
|
||||
1. Ir a: http://localhost:8069
|
||||
2. Iniciar sesión
|
||||
3. Navegar a la tienda: http://localhost:8069/eskaera
|
||||
4. Verificar que los precios mostrados incluyen IVA
|
||||
|
||||
### 3. Test de ejemplo
|
||||
|
||||
**Producto:** Pan integral (ejemplo)
|
||||
- **Precio base:** 2.50 €
|
||||
- **IVA (10%):** 0.25 €
|
||||
- **Precio esperado en tienda:** **2.75 €**
|
||||
|
||||
## Cambios Realizados
|
||||
|
||||
### Archivo modificado: `controllers/website_sale.py`
|
||||
|
||||
1. **Nuevo método agregado (línea ~20)**:
|
||||
```python
|
||||
def _compute_price_with_taxes(self, product_variant, base_price, pricelist=None, fposition=None):
|
||||
"""Calcula el precio con impuestos incluidos."""
|
||||
```
|
||||
|
||||
2. **Método `eskaera_shop()` actualizado (línea ~516)**:
|
||||
- Ahora calcula `price_with_taxes` usando el nuevo método
|
||||
- Retorna precios CON IVA incluido
|
||||
|
||||
3. **Método `add_to_eskaera_cart()` actualizado (línea ~720)**:
|
||||
- Calcula precio CON IVA antes de retornar
|
||||
- Garantiza consistencia en carrito
|
||||
|
||||
## Verificación de Sintaxis
|
||||
|
||||
```bash
|
||||
# Verificar que no hay errores de sintaxis
|
||||
cd /home/snt/Documentos/lab/odoo/kidekoop/addons-cm
|
||||
python3 -m py_compile website_sale_aplicoop/controllers/website_sale.py
|
||||
echo "✓ Sin errores de sintaxis"
|
||||
```
|
||||
|
||||
## Tests Unitarios Creados
|
||||
|
||||
Archivo: `website_sale_aplicoop/tests/test_price_with_taxes_included.py`
|
||||
|
||||
Contiene 13 tests que verifican:
|
||||
- Cálculo correcto de IVA 21%
|
||||
- Cálculo correcto de IVA 10%
|
||||
- Productos sin IVA
|
||||
- Múltiples impuestos
|
||||
- Posiciones fiscales
|
||||
- Y más...
|
||||
|
||||
## Problema Solucionado
|
||||
|
||||
**ANTES:** Los precios mostraban 100.00 € (sin IVA)
|
||||
**DESPUÉS:** Los precios muestran 121.00 € (con IVA 21%)
|
||||
|
||||
✅ **Corrección aplicada exitosamente**
|
||||
314
docs/TRANSLATIONS.md
Normal file
314
docs/TRANSLATIONS.md
Normal file
|
|
@ -0,0 +1,314 @@
|
|||
# Sistema de Traducciones - Guía Completa
|
||||
|
||||
## Estado Actual
|
||||
|
||||
✅ **El sistema de traducciones está funcionando correctamente**
|
||||
|
||||
Todos los addons custom incluyen traducciones para:
|
||||
- **Español (es)** - Obligatorio
|
||||
- **Euskera (eu)** - Obligatorio
|
||||
- Catalán (ca), Gallego (gl), Portugués (pt), Francés (fr), Italiano (it) - Opcional
|
||||
|
||||
## Estructura de Archivos
|
||||
|
||||
Cada addon debe tener la siguiente estructura:
|
||||
|
||||
```
|
||||
addon_name/
|
||||
├── i18n/
|
||||
│ ├── es.po # Español (obligatorio)
|
||||
│ ├── eu.po # Euskera (obligatorio)
|
||||
│ ├── ca.po # Catalán (opcional)
|
||||
│ ├── gl.po # Gallego (opcional)
|
||||
│ ├── pt.po # Portugués (opcional)
|
||||
│ ├── fr.po # Francés (opcional)
|
||||
│ ├── it.po # Italiano (opcional)
|
||||
│ └── addon_name.pot # Template (generado automáticamente)
|
||||
```
|
||||
|
||||
## Reglas Importantes
|
||||
|
||||
### ❌ NO Hacer
|
||||
|
||||
**NUNCA usar `_()` en definiciones de campos a nivel de módulo:**
|
||||
|
||||
```python
|
||||
# ❌ INCORRECTO - Causa warnings
|
||||
from odoo import _
|
||||
|
||||
class MyModel(models.Model):
|
||||
_name = 'my.model'
|
||||
|
||||
name = fields.Char(string=_("Name")) # ❌ MAL
|
||||
description = fields.Text(string=_("Description")) # ❌ MAL
|
||||
```
|
||||
|
||||
**Por qué está mal**: `_()` se ejecuta en tiempo de importación del módulo, antes de que el sistema de traducciones esté inicializado, causando warnings.
|
||||
|
||||
### ✅ Hacer
|
||||
|
||||
**Usar strings literales en definiciones de campos:**
|
||||
|
||||
```python
|
||||
# ✅ CORRECTO
|
||||
from odoo import models, fields
|
||||
|
||||
class MyModel(models.Model):
|
||||
_name = 'my.model'
|
||||
|
||||
name = fields.Char(string="Name") # ✅ BIEN
|
||||
description = fields.Text(string="Description") # ✅ BIEN
|
||||
```
|
||||
|
||||
**Usar `_()` solo en métodos y código ejecutable:**
|
||||
|
||||
```python
|
||||
# ✅ CORRECTO
|
||||
from odoo import _, models
|
||||
|
||||
class MyModel(models.Model):
|
||||
_name = 'my.model'
|
||||
|
||||
def action_confirm(self):
|
||||
message = _("Order confirmed successfully") # ✅ BIEN
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
'params': {
|
||||
'title': _('Success'), # ✅ BIEN
|
||||
'message': message,
|
||||
'type': 'success',
|
||||
}
|
||||
}
|
||||
|
||||
def _compute_display_name(self):
|
||||
for record in self:
|
||||
record.display_name = _("Order %s") % record.name # ✅ BIEN
|
||||
```
|
||||
|
||||
## Cómo Generar/Actualizar Traducciones
|
||||
|
||||
### 1. Exportar Términos Traducibles
|
||||
|
||||
```bash
|
||||
# Exportar términos del addon
|
||||
docker-compose exec odoo odoo \
|
||||
--addons-path=/mnt/extra-addons \
|
||||
--i18n-export=/tmp/addon_name.pot \
|
||||
--modules=addon_name \
|
||||
--db=odoo \
|
||||
--stop-after-init
|
||||
|
||||
# Copiar el archivo generado
|
||||
docker-compose cp odoo:/tmp/addon_name.pot ./addon_name/i18n/
|
||||
```
|
||||
|
||||
### 2. Actualizar Archivos .po Existentes
|
||||
|
||||
```bash
|
||||
cd addon_name/i18n
|
||||
|
||||
# Actualizar español
|
||||
msgmerge --update es.po addon_name.pot
|
||||
|
||||
# Actualizar euskera
|
||||
msgmerge --update eu.po addon_name.pot
|
||||
|
||||
# Actualizar otros idiomas (si existen)
|
||||
msgmerge --update ca.po addon_name.pot
|
||||
msgmerge --update gl.po addon_name.pot
|
||||
```
|
||||
|
||||
### 3. Traducir Términos Nuevos
|
||||
|
||||
Editar los archivos `.po` y completar las traducciones:
|
||||
|
||||
```po
|
||||
#: models/my_model.py:25
|
||||
msgid "Order confirmed successfully"
|
||||
msgstr "Pedido confirmado correctamente" # Español
|
||||
|
||||
#: models/my_model.py:30
|
||||
msgid "Success"
|
||||
msgstr "Éxito"
|
||||
```
|
||||
|
||||
### 4. Cargar Traducciones en Odoo
|
||||
|
||||
```bash
|
||||
# Actualizar addon con nuevas traducciones
|
||||
docker-compose exec odoo odoo -d odoo -u addon_name --stop-after-init
|
||||
|
||||
# O reiniciar Odoo para que cargue los cambios
|
||||
docker-compose restart odoo
|
||||
```
|
||||
|
||||
## Formato de Archivos .po
|
||||
|
||||
### Cabecera Requerida
|
||||
|
||||
```po
|
||||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * addon_name
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 18.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-02-12 10:00+0000\n"
|
||||
"PO-Revision-Date: 2026-02-12 10:00+0000\n"
|
||||
"Last-Translator: Your Name <your.email@example.com>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: es\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
```
|
||||
|
||||
### Campos Traducibles
|
||||
|
||||
Odoo automáticamente detecta y exporta:
|
||||
|
||||
1. **String en definiciones de campos**: `fields.Char(string="Name")`
|
||||
2. **Help text**: `fields.Char(help="Enter the product name")`
|
||||
3. **Selection options**: `fields.Selection([('draft', 'Draft'), ('done', 'Done')])`
|
||||
4. **Texto en vistas XML**: `<label string="Customer Name"/>`
|
||||
5. **Botones en vistas**: `<button string="Confirm"/>`
|
||||
6. **Mensajes en código**: `_("Message text")`
|
||||
7. **Excepciones**: `raise ValidationError(_("Invalid value"))`
|
||||
|
||||
## Verificación
|
||||
|
||||
### Comprobar que las Traducciones Funcionan
|
||||
|
||||
1. **Activar el idioma en Odoo**:
|
||||
- Settings > Translations > Load a Translation
|
||||
- Seleccionar el idioma (ej: Español)
|
||||
|
||||
2. **Cambiar idioma de usuario**:
|
||||
- Settings > Users > [Usuario]
|
||||
- Language: Español
|
||||
|
||||
3. **Verificar en la interfaz**:
|
||||
- Abrir el módulo
|
||||
- Verificar que los textos aparecen traducidos
|
||||
|
||||
### Logs de Carga
|
||||
|
||||
Al actualizar el módulo, verificar en logs:
|
||||
|
||||
```bash
|
||||
docker-compose logs odoo | grep "loading translation"
|
||||
```
|
||||
|
||||
Debe mostrar:
|
||||
|
||||
```
|
||||
odoo.modules.loading: loading translation file for language 'es': addon_name/i18n/es.po
|
||||
odoo.modules.loading: loading translation file for language 'eu': addon_name/i18n/eu.po
|
||||
```
|
||||
|
||||
## Addons con Traducciones Completas
|
||||
|
||||
### Addons Custom
|
||||
|
||||
| Addon | es | eu | Otros |
|
||||
|-------|----|----|-------|
|
||||
| account_invoice_triple_discount_readonly | ✅ | ✅ | - |
|
||||
| product_price_category_supplier | ✅ | ✅ | - |
|
||||
| product_sale_price_from_pricelist | ✅ | - | - |
|
||||
| website_sale_aplicoop | ✅ | ✅ | ca, gl, pt, fr, it |
|
||||
|
||||
### Addons OCA
|
||||
|
||||
Los addons OCA ya incluyen traducciones en múltiples idiomas.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Warning: "_() called at import time"
|
||||
|
||||
**Síntoma**:
|
||||
```
|
||||
WARNING: _() called at import time at module.path:line
|
||||
```
|
||||
|
||||
**Causa**: `_()` usado en definición de campo a nivel de módulo
|
||||
|
||||
**Solución**: Eliminar `_()` de la definición del campo:
|
||||
```python
|
||||
# Cambiar:
|
||||
name = fields.Char(string=_("Name"))
|
||||
# Por:
|
||||
name = fields.Char(string="Name")
|
||||
```
|
||||
|
||||
### Traducciones No Aparecen
|
||||
|
||||
**Posibles causas**:
|
||||
|
||||
1. **Archivo .po mal formado**: Verificar encoding UTF-8
|
||||
2. **Módulo no actualizado**: `docker-compose exec odoo odoo -d odoo -u addon_name --stop-after-init`
|
||||
3. **Idioma no activado**: Settings > Translations > Load Translation
|
||||
4. **Usuario con idioma incorrecto**: Verificar preferencias de usuario
|
||||
5. **Cache**: Reiniciar Odoo: `docker-compose restart odoo`
|
||||
|
||||
### Caracteres Especiales
|
||||
|
||||
Usar escape correcto en .po:
|
||||
|
||||
```po
|
||||
# Comillas
|
||||
msgstr "Haga clic en \"Confirmar\""
|
||||
|
||||
# Saltos de línea
|
||||
msgstr "Primera línea\n"
|
||||
"Segunda línea"
|
||||
|
||||
# Caracteres especiales (á, é, í, ó, ú, ñ, ç)
|
||||
# Usar directamente, el archivo debe ser UTF-8
|
||||
msgstr "Configuración"
|
||||
```
|
||||
|
||||
## Herramientas Útiles
|
||||
|
||||
### Editores de .po
|
||||
|
||||
- **Poedit**: https://poedit.net/ (GUI, recomendado)
|
||||
- **VS Code**: Con extensión "gettext" para syntax highlighting
|
||||
- **Lokalize**: Editor KDE para traducciones
|
||||
|
||||
### Comandos Útiles
|
||||
|
||||
```bash
|
||||
# Verificar formato de archivo .po
|
||||
msgfmt -c -v -o /dev/null addon_name/i18n/es.po
|
||||
|
||||
# Estadísticas de traducción
|
||||
msgfmt --statistics addon_name/i18n/es.po
|
||||
|
||||
# Extraer solo términos sin traducir
|
||||
msgattrib --untranslated addon_name/i18n/es.po
|
||||
```
|
||||
|
||||
## Mejores Prácticas
|
||||
|
||||
1. ✅ Mantener es.po y eu.po siempre actualizados
|
||||
2. ✅ Usar términos consistentes en todo el proyecto
|
||||
3. ✅ Incluir contexto en comentarios cuando sea necesario
|
||||
4. ✅ Verificar traducciones antes de commit
|
||||
5. ✅ Nunca usar `_()` en definiciones de campos
|
||||
6. ✅ Usar encoding UTF-8 en todos los archivos .po
|
||||
7. ✅ Generar .pot después de cambios importantes
|
||||
8. ✅ Documentar términos técnicos específicos del dominio
|
||||
|
||||
## Referencias
|
||||
|
||||
- **Odoo Translation Guidelines**: https://www.odoo.com/documentation/18.0/developer/reference/frontend/translations.html
|
||||
- **GNU gettext Manual**: https://www.gnu.org/software/gettext/manual/
|
||||
- **OCA Translation Guidelines**: https://github.com/OCA/maintainer-tools/wiki/Translations
|
||||
|
||||
---
|
||||
|
||||
**Última Actualización**: 2026-02-12
|
||||
Loading…
Add table
Add a link
Reference in a new issue