Files
eru/README.md
T

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.