BCV Exchange Rate Plugin for iDempiere v10
Descripción
Plugin OSGi que obtiene automáticamente la tasa de cambio oficial del BCV (Banco Central de Venezuela) USD/VES y la registra en la tabla C_Conversion_Rate de iDempiere.
Características
- Doble fuente de datos: API bcv.today + scraping directo del sitio web del BCV
- Fallback automático: Si la API no tiene datos recientes, scrapea el sitio del BCV
- Busca hacia atrás cuando el BCV no publica tasa para un día hábil
- Rellena automáticamente días sin publicación (fines de semana, feriados)
- Ejecución manual con rango de fechas específico
- Ejecución automática que detecta días pendientes y los rellena
- Valida duplicados antes de insertar
- Modo simulación para pruebas sin guardar
- Scheduler automático diario
Fuentes de Datos
Fuente Principal: API bcv.today
- Endpoint:
https://bcv.today/api/v1/rate.json - Endpoint por fecha:
https://bcv.today/api/v1/history/YYYY-MM-DD.json - Ventaja: Rápida, estructurada
- Desventaja: Puede tener retraso deactualización
Fuente de Respaldo: Scraping BCV
- URL:
https://www.bcv.org.ve/ - Método: Extracción directa del HTML con regex
- Ventaja: Siempre actualizada (datos oficiales)
- Desventaja: Requiere SSL bypass y parsing de HTML
Flujo de Selección
1. Intentar API bcv.today (búsqueda hacia adelante 1-10 días)
↓ (si no encuentra)
2. Scraping directo del sitio web del BCV
↓ (si falla)
3. API bcv.today getCurrentRate() (último recurso)
Comportamiento del Proceso
Ejecución Manual (con parámetros de fecha)
- Procesa cada día en el rango DateFrom → DateTo
- Para cada día, busca la última tasa BCV hacia atrás (hasta 10 días)
- Usa la tasa encontrada pero asigna ValidFrom = fecha solicitada
Ejecución Automática (sin parámetros de fecha)
- Obtiene la última tasa BCV publicada (API + scraping)
- Compara con la última tasa en BD
- Si hay tasa nueva no registrada, procesa desde la última en BD hasta esa fecha
- Si ya están todas → "No hay días pendientes"
Lógica de Fallback por Día
| Situación | Acción |
|---|---|
| BCV publica tasa para la fecha | Usa esa tasa, ValidFrom = fecha solicitada |
| BCV no publica (feriado, fin de semana) | Busca hacia atrás, usa última tasa disponible |
| No se encuentra tasa en 10 días | Registra error |
Ejemplo
| Fecha solicitada | BCV publica | Tasa usada | ValidFrom |
|---|---|---|---|
| 24/06 (miércoles) | No | 617.6388 (del 23/06) | 24/06 |
| 25/06 (jueves) | Sí (621.5299) | 621.5299 | 25/06 |
| 27/06 (sábado) | No | tasa del 26/06 | 27/06 |
| 28/06 (domingo) | No | tasa del 26/06 | 28/06 |
Archivos del Plugin
com.venezuela.bcvrate/
├ META-INF/
│ └ MANIFEST.MF # Manifest OSGi (Bundle-Activator)
├ src/com/venezuela/bcvrate/
│ ├ Activator.java # Registra IProcessFactory via BundleContext
│ ├ factory/
│ │ └ BCVProcessFactory.java # Factory del proceso
│ ├ process/
│ │ └ BCVExchangeRateProcess.java # Lógica principal del proceso
│ └ service/
│ ├ BCVApiService.java # Cliente HTTP + scraping BCV
│ └ BCVRateResponse.java # Modelo de respuesta
├ dist/
│ └ com.venezuela.bcvrate.jar # JAR compilado listo para instalar
├ lib/
│ └ org.osgi.framework.jar # Dependencia OSGi
├ migration/
│ ├ install_v10.sql # SQL para registrar proceso
│ └ BCV_ExchangeRateProcess_2Pack.xml # 2pack para despliegue
└ build.sh # Script de build para Linux
Instalación
Prerrequisitos
- iDempiere v10 ejecutándose
- Acceso a consola Felix (http://servidor:8080/system/console)
- Acceso a la base de datos PostgreSQL
Paso 1: Instalar JAR en Felix
- Abrir consola Felix → Bundles
- Click Install/Update Bundle
- Seleccionar
com.venezuela.bcvrate.jar - Seleccionar Start
- Verificar estado Active
Importante: Si actualizas el plugin, debes uninstall el bundle anterior primero, luego install el nuevo. Equinox no recarga si la versión es la misma.
Paso 2: Registrar Proceso
Opción A: Ejecutar migration/install_v10.sql en la BD
Opción B: Crear manualmente via UI:
- Proceso: BCV Exchange Rate Update
- ClassName: com.venezuela.bcvrate.process.BCVExchangeRateProcess
- AccessLevel: 3 (Client + Organization)
- Parámetros: Client, Conversion Type, Date From, Date To, Simulation Mode
Opción C: Importar 2pack migration/BCV_ExchangeRateProcess_2Pack.xml
Paso 3: Agregar al Menú
Crear registro en AD_TreeBar o importar 2pack con el menú configurado.
Paso 4: Configurar Scheduler (Agenda)
- Ir a Agenda (Process Scheduler)
- Crear nuevo registro
- Seleccionar proceso: BCV Exchange Rate Update
- Frecuencia: 1 dia
- Hora: 17:00-18:00 (cuando BCV publica la tasa)
- Dejar parámetros de fecha vacíos
Recomendación: Configurar dos ejecuciones (17:00 y 21:00) por si la primera no captura la tasa.
Parámetros del Proceso
| Parámetro | Tipo | Requerido | Default | Descripción |
|---|---|---|---|---|
| Grupo Empresarial | List | Sí | @#AD_Client_ID@ | Cliente de iDempiere |
| Tipo de Conversión | List | Sí | BCV | Tipo de conversión |
| Fecha desde | Date | No | (vacío) | Fecha inicio (vacío = auto) |
| Fecha hasta | Date | No | (vacío) | Fecha fin (vacío = auto) |
| Simulación | Yes/No | No | N | Ejecutar sin guardar cambios |
Datos Clave
| Campo | Valor | Descripción |
|---|---|---|
| C_Currency_ID | 100 | USD (hardcoded) |
| C_Currency_ID_To | (dinámico) | Moneda funcional del cliente |
| AD_Org_ID | 0 | Todas las organizaciones |
| MultiplyRate | (API/scraping) | Tasa USD → VES (4 decimales) |
| DivideRate | 1/MultiplyRate | Tasa VES → USD (10 decimales) |
| ValidFrom | (fecha solicitada) | Fecha que necesita la empresa |
| ValidTo | (fecha solicitada) | Misma que ValidFrom |
Solución de Problemas
Error "Failed to create new process instance"
- Verificar que el bundle está Active en Felix
- Verificar logs:
grep -i bcvrate /opt/idempiere-server/logs/idempiere.log
Error "Cross tenant PO writing"
- Se resolvió usando
set_ValueNoCheck()en vez desetMultiplyRate() - El proceso crea el registro con el client ID correcto del contexto
Error "No se pudo obtener la moneda funcional"
- Verificar que el cliente tiene un esquema contable configurado
- Verificar que la moneda funcional no sea USD
API bcv.today no responde o tiene retraso
- Verificar conectividad:
curl https://bcv.today/api/v1/rate.json - El plugin automáticamente intenta scraping del sitio web del BCV como respaldo
Error SSL en scraping del BCV
- El plugin incluye SSL bypass para el sitio web del BCV
- Si persiste, verificar que el bundle tiene la última versión
Tasa con decimales incorrectos
- Se resolvió usando
set_ValueNoCheck()en vez de setters de MConversionRate - MultiplyRate: 4 decimales
- DivideRate: 10 decimales
Scraping captura tasa incorrecta (EUR en vez de USD)
- El regex busca específicamente después de
USD</span>en el HTML - Verificar que el HTML del BCV no ha cambiado su estructura
Compilación
En Windows (desarrollo)
# Requiere: JDK 11, JARs de iDempiere v10
$javac = "C:\Program Files\Eclipse Adoptium\jdk-11.0.31.11-hotspot\bin\javac.exe"
$v10Plugins = "path/to/idempiere-v10/plugins"
$sources = Get-ChildItem -Path src -Recurse -Filter "*.java"
$classpath = "$v10Plugins\org.adempiere.base_*.jar;$v10Plugins\json_*.jar;lib\org.osgi.framework.jar"
& $javac -cp $classpath -d dist -source 11 -target 11 @($sources)
& jar cfm dist/com.venezuela.bcvrate.jar META-INF/MANIFEST.MF -C dist .
En Linux (servidor)
chmod +x build.sh
./build.sh
Dependencias
- org.adempiere.base >= 10.0.0 - Framework iDempiere
- json >= 20190722.0.0 - Parsing JSON (solo para API bcv.today)
- org.osgi.framework - Framework OSGi (sistema)
Notas Técnicas
Dual Source: API + Scraping
El plugin implementa dos fuentes de datos para máxima confiabilidad:
- API bcv.today: Consulta REST rápida, pero puede tener retraso deactualización
- Scraping BCV: Extracción directa del HTML del sitio web oficial
El flujo automático intenta la API primero y fallback al scraping si no hay datos recientes.
SSL Bypass para Scraping
El sitio web del BCV usa certificados SSL que pueden no estar en el cacerts de Java. El plugin implementa SSL bypass por-conexión (no global) para evitar conflictos con iDempiere:
TrustManager[] trustAllCerts = new TrustManager[]{...};
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(sc.getSocketFactory());
conn.setHostnameVerifier((hostname, session) -> true);
Extracción de Tasa USD del HTML
El regex busca específicamente la tasa del dólar después del elemento USD</span>:
USD</span>.*?strong-tb">\s*([\d.,]+)\s*</strong>
Esto evita capturar otras tasas (EUR, CNY, etc.) que aparecen en la misma página.
Por qué se usa BundleActivator en vez de DS
component.xml(Declarative Services) no funcionó en iDempiere v10@Componentannotation tampoco funcionóBundleActivatorconBundleContext.registerService()es el más confiable- Requiere
Import-Package: org.osgi.frameworken MANIFEST.MF
Por qué se usa regex en vez del JSON library
- El
json_20190722.0.0.jarbundled con iDempiere v10 tiene bugs de precisión getDouble("USD")yget("USD").toString()corrompen decimales- Se extrae el valor directamente del string raw con regex:
"USD"\s*:\s*([0-9.]+)
Por qué se usa set_ValueNoCheck en vez de setters
MConversionRate.setMultiplyRate()modifica el valor internamenteMConversionRate.setDivideRate()redondea a 4 decimalesset_ValueNoCheck()bypassa la lógica interna y almacena el valor exacto
Cross Tenant
MConversionRate.setRate()intenta guardar con AD_Client_ID=0- Se crea el registro directamente con
MConversionRatey se usaset_ValueNoCheck("AD_Client_ID", ...)
Por qué el JAR debe cambiar de versión
- Equinox (runtime OSGi) cachea bundles por versión
- Si la versión no cambia, el bundle anterior permanece activo
- Siempre incrementar
Bundle-Versionen MANIFEST.MF al actualizar
Historial de Cambios
v1.0.11 (2026-07-03)
- Fix regex scraping: captura tasa USD específicamente (no EUR)
- Regex busca después de
USD</span>en el HTML
v1.0.10 (2026-07-03)
- Logging mejorado en proceso para ver fuente de datos
- Scraping status visible en output del proceso
v1.0.9 (2026-07-03)
- SSL bypass por-conexión (no global) para scraping BCV
- Logging detallado en getLatestRate()
v1.0.8 (2026-07-03)
- Scraping directo del sitio web del BCV como fuente de respaldo
- Dual source: API bcv.today + scraping bcv.org.ve
- Método getRateFromBCVWebsite() con extracción regex del HTML
v1.0.7 (2026-06-23)
- Lógica automática: detecta días pendientes entre BD y última tasa BCV
- Ejecución sin parámetros: busca última tasa BCV y rellena huecos
v1.0.6 (2026-06-23)
- Fix: ejecución automática ahora detecta si hay tasa para hoy
- Evita procesar si ya existen todas las tasas
v1.0.5 (2026-06-23)
- Lógica de fallback: busca hacia atrás cuando BCV no publica tasa
- Rellena días sin publicación con última tasa disponible
v1.0.4 (2026-06-23)
- getRateForDateWithFallback(): busca hacia atrás hasta 10 días
- BCVRateResponse: campo requestedDate vs effectiveDate
v1.0.3 (2026-06-23)
- DivideRate: 10 decimales en vez de 4
v1.0.2 (2026-06-23)
- Todos los setters usan set_ValueNoCheck() para bypass MConversionRate
- Log de verificación post-save
v1.0.1 (2026-06-23)
- Regex para extraer USD directo del string raw (bypass JSON library)
v1.0.0 (2026-06-22)
- Versión inicial
- API: bcv.today
- BundleActivator para registro de IProcessFactory
Licencia
Plugin personalizado para uso interno de Ezerpa.