Skip to content

Commit 24c68da

Browse files
committed
feat: add support for EUR currency and enhance exchange rate handling across components
1 parent 67f7567 commit 24c68da

13 files changed

Lines changed: 251 additions & 88 deletions

File tree

functions/lib/exchangeRates.js

Lines changed: 29 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

functions/lib/exchangeRates.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

functions/src/exchangeRates.ts

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ if (!admin.apps.length) {
99
admin.initializeApp();
1010
}
1111

12-
/** Scrape the BCV official USD rate directly from bcv.org.ve */
13-
async function scrapeBCVRate(): Promise<number | null> {
12+
/** Scrape the BCV official USD and EUR rates directly from bcv.org.ve */
13+
async function scrapeBCVRates(): Promise<{ usd: number; eur: number } | null> {
1414
try {
1515
const httpsAgent = new https.Agent({ rejectUnauthorized: false });
1616
const response = await axios.get('https://www.bcv.org.ve/', {
@@ -24,22 +24,34 @@ async function scrapeBCVRate(): Promise<number | null> {
2424
});
2525

2626
const $ = cheerio.load(response.data);
27-
const rateText = $('#dolar .centrado strong').text().trim();
2827

29-
if (!rateText) {
30-
logger.error('Could not find USD rate on BCV website');
31-
return null;
28+
function parseRate(selector: string, label: string): number | null {
29+
const text = $(selector).text().trim();
30+
if (!text) {
31+
logger.error(`Could not find ${label} rate on BCV website`);
32+
return null;
33+
}
34+
const rate = parseFloat(text.replace(',', '.'));
35+
if (isNaN(rate) || rate <= 0) {
36+
logger.error(`Failed to parse BCV ${label} rate: "${text}"`);
37+
return null;
38+
}
39+
return rate;
3240
}
3341

34-
// BCV uses comma as decimal separator, e.g. "459,4525"
35-
const rate = parseFloat(rateText.replace(',', '.'));
36-
if (isNaN(rate) || rate <= 0) {
37-
logger.error(`Failed to parse BCV rate: "${rateText}"`);
38-
return null;
42+
const usd = parseRate('#dolar .centrado strong', 'USD');
43+
const eur = parseRate('#euro .centrado strong', 'EUR');
44+
45+
if (!usd) return null;
46+
47+
// EUR is optional — USD is required
48+
if (eur) {
49+
logger.info(`Scraped BCV rates: 1 USD = ${usd} VES, 1 EUR = ${eur} VES`);
50+
return { usd, eur };
3951
}
4052

41-
logger.info(`Scraped BCV rate: 1 USD = ${rate} VES`);
42-
return rate;
53+
logger.warn('EUR rate not found, using USD-derived estimate');
54+
return { usd, eur: usd * 1.08 };
4355
} catch (error) {
4456
logger.error('Error scraping BCV website:', error);
4557
return null;
@@ -55,21 +67,21 @@ export const fetchExchangeRates = onSchedule(
5567
async () => {
5668
logger.info('Fetching BCV exchange rates...');
5769

58-
let usdToVes = await scrapeBCVRate();
59-
let source = 'bcv.org.ve (scrape)';
70+
const bcvRates = await scrapeBCVRates();
71+
const source = 'bcv.org.ve (scrape)';
6072

61-
if (!usdToVes) {
62-
logger.error('BCV scrape failed — no rate obtained');
73+
if (!bcvRates) {
74+
logger.error('BCV scrape failed — no rates obtained');
6375
return;
6476
}
6577

6678
const db = admin.firestore();
6779
const now = admin.firestore.Timestamp.now();
6880

6981
await db.collection('exchangeRates').add({
70-
rates: { USD: usdToVes },
82+
rates: { USD: bcvRates.usd, EUR: bcvRates.eur },
7183
source,
7284
recordedAt: now,
7385
});
74-
logger.info(`Stored BCV rate: 1 USD = ${usdToVes} VES (source: ${source})`);
86+
logger.info(`Stored BCV rates: 1 USD = ${bcvRates.usd} VES, 1 EUR = ${bcvRates.eur} VES (source: ${source})`);
7587
});

functions/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"module": "commonjs",
44
"noImplicitReturns": true,
55
"noUnusedLocals": false,
6+
"rootDir": "src",
67
"outDir": "lib",
78
"sourceMap": true,
89
"strict": true,

0 commit comments

Comments
 (0)