Herramienta en Python para extraer datos de Odoo mediante XML-RPC y exportarlos a archivos CSV, con soporte especial para enriquecer reportes de ausencias (hr.leave.report) con información semanal del módulo OCA date.range.
🎯 Descripción general
Este proyecto se conecta a una instancia de Odoo a través del protocolo XML-RPC, autentica al usuario, consulta uno o varios modelos configurables y guarda los resultados en archivos CSV con marca de tiempo dentro de la carpeta output/.
Está diseñado con dos flujos de extracción:
- Extracción genérica — para cualquier modelo de Odoo (res.partner, sale.order, etc.).
- Extracción especializada de hr.leave.report — enriquece cada registro de ausencia con el número de semana, nombre de rango y año tomados del módulo OCA date.range. Si no encuentra coincidencia, calcula la semana ISO a partir de la fecha como respaldo.
📁 Estructura del proyecto
extract_data/ ├── .env ← credenciales (no compartir) ├── .env.example ← plantilla de credenciales ├── README.md ← documentación breve original ├── DOCUMENTACION.md ← este archivo (documentación extendida) ├── models_config.py ← configuración de modelos, campos y filtros ├── odoo_extractor.py ← script principal ├── requirements.txt ← dependencias de Python ├── setup.sh ← script de instalación automática ├── output/ ← carpeta donde se guardan los CSV generados └── venv/ ← entorno virtual de Python⚙️ Requisitos
- Python 3.8+
- Acceso a una instancia de Odoo con API XML-RPC habilitada
- Usuario con permisos de lectura sobre los modelos a extraer
- Módulo OCA date.range instalado en Odoo (solo si se usa la extracción de hr.leave.report con enriquecimiento de semanas)
Dependencias (requirements.txt)
python-dotenv>=1.0.0
El módulo xmlrpc.client forma parte de la librería estándar de Python, no requiere instalación adicional.
🚀 Instalación
Opción A — Automática (recomendada)
bash setup.sh
El script setup.sh realiza lo siguiente:
- Crea un entorno virtual venv/.
- Activa el entorno virtual.
- Actualiza pip.
- Instala las dependencias de requirements.txt.
- Crea la carpeta output/ si no existe.
- Copia .env.example a .env si no está presente.
Opción B — Manual
python3 -m venv venv source venv/bin/activate pip install -r requirements.txt cp .env.example .env mkdir -p output
🔐 Configuración de credenciales
Editar el archivo .env con los datos de conexión a Odoo:
ODOO_URL=https://tu-instancia-odoo.com ODOO_DB=nombre_de_base_de_datos ODOO_USER=usuario@correo.com ODOO_PASSWORD=tu_contraseña
⚠️ Nunca subas el archivo .env a un repositorio público. Agrégalo a .gitignore.
🧩 Configuración de modelos (models_config.py)
Este archivo define qué extraer de Odoo. Tiene tres elementos principales:
1. MODELS_CONFIG — Modelos y campos
Diccionario donde cada clave es el nombre técnico del modelo de Odoo y el valor es la lista de campos a extraer.
MODELS_CONFIG = {
"hr.leave.report": [
"id",
"name",
"employee_id",
"leave_type",
"category_id",
"holiday_type",
"department_id",
"holiday_status_id",
"date_from",
"date_to",
"number_of_days",
],
# "res.partner": ["id", "name", "email", "phone"],
}
📌 Importante: los campos week_number, week_name y week_year se agregan automáticamente en la extracción de hr.leave.report. No se deben listar aquí.
2. MODELS_FILTERS — Filtros por modelo
Filtros en formato de dominio Odoo (lista de tripletas [campo, operador, valor]).
MODELS_FILTERS = {
"hr.leave.report": [
["state", "in", ["validate", "validate1"]], # solo ausencias aprobadas
["date_from", ">=", "2026-01-01"], # desde inicio de año
],
}
Si un modelo no aparece en este diccionario o tiene filtros [], se traerán todos los registros.
3. MAX_RECORDS — Límite de registros
MAX_RECORDS = 0 # 0 = sin límite; usar 100 para pruebas
▶️ Ejecución
source venv/bin/activate python odoo_extractor.py
Salida esperada en consola
======================================================= Odoo Data Extractor — hr.leave.report + Semanas ======================================================= 12:34:56 [INFO] Connecting to https://tu-odoo.com | DB: mi_db | User: correo@... 12:34:57 [INFO] Authenticated successfully (uid=7) ▶ Model: hr.leave.report Fetching 'hr.leave.report' fields=[...] filter=[...] ... → 245 records retrieved ▶ Fetching week ranges from date.range (OCA)... → Found weekly range type(s): ['Semanas'] → 260 date ranges loaded → 243/245 records matched to a week range ✓ Saved → output/hr_leave_report_20260416_123501.csv ======================================================= Done! Archivos guardados en ./output/ =======================================================
🗂️ Salida (archivos CSV)
Cada modelo genera un archivo CSV independiente en la carpeta output/, con timestamp para evitar sobrescribir ejecuciones previas:
output/hr_leave_report_20260416_123501.csv output/res_partner_20260416_123502.csv
Transformaciones aplicadas al guardar
| Tipo de campo Odoo | Valor original | Valor en CSV |
|---|---|---|
| Many2one (ej. employee_id) | [7, 'Juan Pérez'] | Juan Pérez |
| Campo vacío | False | "" (cadena vacía) |
| Fecha / fecha-hora | '2026-03-15' o '2026-03-15 10:30:00' | sin cambios |
| Otros valores | tal cual | tal cual |
🧠 Funciones principales (odoo_extractor.py)
connect_odoo()
Carga credenciales desde .env, autentica contra el endpoint /xmlrpc/2/common y devuelve el proxy de modelos junto al uid, db y password necesarios para las llamadas posteriores a /xmlrpc/2/object.
flatten_value(value)
Normaliza los valores devueltos por Odoo. Convierte los campos Many2one [id, 'Nombre'] en solo 'Nombre', y los False en cadena vacía.
parse_odoo_date(date_str)
Convierte cadenas de fecha de Odoo ('YYYY-MM-DD' o 'YYYY-MM-DD HH:MM:SS') a objetos date de Python, tolerando valores vacíos o mal formateados.
fetch_week_ranges(models, uid, db, password)
Consulta los tipos de rango disponibles en date.range.type y filtra aquellos cuyo nombre contenga palabras como week, semana, wk o sem. Luego carga todos los registros de date.range de esos tipos, extrae el número de semana desde el nombre del rango (ej. "Semana 10 2026") o, si no es posible, lo calcula con isocalendar(). Devuelve una lista ordenada por fecha de inicio para permitir búsqueda rápida.
find_week_for_date(record_date, week_ranges)
Dado una fecha y la lista de rangos semanales, devuelve el diccionario de la semana donde cae la fecha o None si no hay coincidencia.
fetch_model(models, uid, db, password, model_name, fields, filters, limit)
Ejecuta search_read sobre un modelo arbitrario de Odoo y devuelve la lista de diccionarios resultante.
save_to_csv(records, model_name, fields, extra_fields=None)
Escribe los registros a un CSV con encabezado, aplicando flatten_value a cada celda. Acepta columnas extra (usadas para week_number, week_name, week_year).
extract_leave_report(models, uid, db, password)
Flujo especializado: descarga los registros de hr.leave.report, obtiene los rangos semanales del módulo date.range, empareja cada ausencia con su semana correspondiente usando date_from como fecha de referencia y agrega tres columnas (week_number, week_name, week_year). Si un registro no coincide con ningún rango OCA, calcula la semana ISO como respaldo.
extract_generic(models, uid, db, password, model_name)
Flujo estándar sin enriquecimiento, aplicado a cualquier modelo distinto de hr.leave.report.
main()
Punto de entrada. Itera sobre MODELS_CONFIG y decide qué flujo usar por modelo, capturando errores por modelo para que un fallo no detenga el resto de la extracción.
🔄 Flujo de ejecución (resumen)
- Se cargan variables de entorno desde .env.
- Se autentica contra Odoo vía XML-RPC.
- Por cada modelo en MODELS_CONFIG:
- Si es hr.leave.report → se ejecuta el flujo enriquecido con semanas OCA.
- En caso contrario → se ejecuta el flujo genérico.
- Se guarda un CSV por modelo con timestamp en output/.
- Los errores por modelo se registran pero no abortan la ejecución.
🛠️ Casos de uso comunes
Agregar un nuevo modelo a extraer
En models_config.py:
MODELS_CONFIG = {
"hr.leave.report": [...],
"res.partner": ["id", "name", "email", "phone", "country_id"],
"sale.order": ["id", "name", "partner_id", "date_order", "amount_total", "state"],
}
MODELS_FILTERS = {
"sale.order": [["state", "=", "sale"]],
}
Probar con pocos registros
MAX_RECORDS = 50
Cambiar la fecha de referencia para el cálculo de la semana
En extract_leave_report(), cambiar la línea:
date_field = "date_from" # o "date_to"
⚠️ Consideraciones y notas
- hr.leave.report es una vista SQL de Odoo, por lo que es solo lectura. Este script no la modifica.
- Los errores de autenticación o de credenciales faltantes se reportan con mensajes claros y el script termina sin continuar.
- Un fallo en la extracción de un modelo (xmlrpc.client.Fault u otras excepciones) no interrumpe el procesamiento de los modelos restantes.
- Si el módulo OCA date.range no está instalado o no hay tipos con nombre tipo "semana/week", el script cae en la estrategia de respaldo usando isocalendar().
- El archivo .env debe tener permisos restrictivos (chmod 600 .env) para proteger las credenciales.
🧪 Solución de problemas
| Síntoma | Causa probable | Solución |
|---|---|---|
| Missing credentials. Check your .env file | Variables vacías en .env | Completar ODOO_URL, ODOO_DB, ODOO_USER, ODOO_PASSWORD |
| Authentication failed | Usuario/contraseña incorrectos o XML-RPC deshabilitado | Verificar credenciales y que el endpoint /xmlrpc/2/common responda |
| Odoo error on 'xxx': Object xxx doesn't exist | Nombre de modelo mal escrito | Revisar el nombre técnico en Odoo (Ajustes → Técnico → Modelos) |
| No weekly date range type found | Módulo date.range no instalado o sin tipos semanales | Instalar el módulo OCA date.range y crear un tipo "Semanas" |
| CSV vacío | Filtros demasiado restrictivos en MODELS_FILTERS | Ajustar el dominio o usar [] temporalmente |
📄 Licencia y autoría
Proyecto interno para extracción de datos de Odoo. Adaptar y redistribuir según las necesidades del equipo.