383 lines
14 KiB
Markdown
383 lines
14 KiB
Markdown
# 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
|
|
- **Reintentos automáticos**: Hasta 3 intentos con espera de 5 segundos entre ellos
|
|
- **Timeout extendido**: 30s conexión / 60s lectura para redes lentas
|
|
- **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
|
|
|
|
### 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 (hasta 3 reintentos)
|
|
↓ (si falla)
|
|
3. API bcv.today getCurrentRate() (último recurso)
|
|
```
|
|
|
|
### 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 de actualizació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
|
|
- **Reintentos**: 3 máximo con espera de 5 segundos
|
|
|
|
## Configuración de Red
|
|
|
|
### Timeouts
|
|
| Operación | Valor | Descripción |
|
|
|-----------|-------|-------------|
|
|
| Conexión | 30 segundos | Tiempo máximo para establecer conexión |
|
|
| Lectura | 60 segundos | Tiempo máximo para recibir datos |
|
|
| Reintentos | 3 | Intentos máximos antes de fallar |
|
|
| Entre reintentos | 5 segundos | Espera entre intentos |
|
|
|
|
### SSL Bypass
|
|
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.
|
|
|
|
## 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)
|
|
1. Obtiene la última tasa BCV publicada (API + scraping)
|
|
2. Compara con la última tasa en BD
|
|
3. Si hay tasa nueva no registrada, procesa desde la última en BD hasta esa fecha
|
|
4. 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 + reintentos
|
|
│ └ 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
|
|
├ .gitignore # Archivos ignorados por git
|
|
└ 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
|
|
- Conectividad a internet (bcv.today y bcv.org.ve)
|
|
|
|
### Paso 1: Instalar JAR en Felix
|
|
1. Abrir consola Felix → **Bundles**
|
|
2. Click **Install/Update Bundle**
|
|
3. Seleccionar `com.venezuela.bcvrate.jar`
|
|
4. Seleccionar **Start**
|
|
5. 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)
|
|
1. Ir a **Agenda** (Process Scheduler)
|
|
2. Crear nuevo registro
|
|
3. Seleccionar proceso: **BCV Exchange Rate Update**
|
|
4. Frecuencia: **1 dia**
|
|
5. Hora: **17:00-18:00** (cuando BCV publica la tasa)
|
|
6. 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 de `setMultiplyRate()`
|
|
- 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
|
|
|
|
### Scraping BCV falla por timeout
|
|
- El plugin reintenta hasta 3 veces con espera de 5 segundos
|
|
- Verificar conectividad: `curl https://www.bcv.org.ve/`
|
|
- Si hay proxy, configurarlo en el servidor
|
|
- El log muestra "Scraping BCV fallo despues de 3 intentos"
|
|
|
|
### 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)
|
|
```powershell
|
|
# 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)
|
|
```bash
|
|
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:
|
|
|
|
1. **API bcv.today**: Consulta REST rápida, pero puede tener retraso de actualización
|
|
2. **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.
|
|
|
|
### Lógica de Reintentos
|
|
Cuando el scraping falla (timeout, error de conexión), el plugin reintenta automáticamente:
|
|
|
|
```java
|
|
int maxRetries = 3;
|
|
for (int attempt = 1; attempt <= maxRetries; attempt++) {
|
|
// Intentar scraping
|
|
// Si falla, esperar 5 segundos y reintentar
|
|
// Si falla 3 veces, registrar error
|
|
}
|
|
```
|
|
|
|
### 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:
|
|
|
|
```java
|
|
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
|
|
- `@Component` annotation tampoco funcionó
|
|
- `BundleActivator` con `BundleContext.registerService()` es el más confiable
|
|
- Requiere `Import-Package: org.osgi.framework` en MANIFEST.MF
|
|
|
|
### Por qué se usa regex en vez del JSON library
|
|
- El `json_20190722.0.0.jar` bundled con iDempiere v10 tiene bugs de precisión
|
|
- `getDouble("USD")` y `get("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 internamente
|
|
- `MConversionRate.setDivideRate()` redondea a 4 decimales
|
|
- `set_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 `MConversionRate` y se usa `set_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-Version` en MANIFEST.MF al actualizar
|
|
|
|
## Repositorio
|
|
|
|
El código fuente está disponible en:
|
|
- **Gitea**: https://gitea.inforcloud.net/ezerpa/eru
|
|
- **Branch principal**: master
|
|
|
|
### Clonar repositorio
|
|
```bash
|
|
git clone https://gitea.inforcloud.net/ezerpa/eru.git
|
|
```
|
|
|
|
### Actualizar código
|
|
```bash
|
|
git pull
|
|
```
|
|
|
|
## Historial de Cambios
|
|
|
|
### v1.0.13 (2026-07-03)
|
|
- Lógica de reintentos: 3 intentos máximo con espera de 5 segundos
|
|
- Timeout incrementado: 30s conexión / 60s lectura
|
|
- Logging detallado de reintentos en servidor
|
|
|
|
### v1.0.12 (2026-07-03)
|
|
- Incremento de timeout HTTP a 30s/60s
|
|
|
|
### 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.
|