127 lines
5.2 KiB
JavaScript
127 lines
5.2 KiB
JavaScript
const axios = require('axios');
|
|
const {DOMParser} = require('xmldom');
|
|
const UserError = require('vn-loopback/util/user-error');
|
|
|
|
module.exports = Self => {
|
|
Self.remoteMethod('exchangeRateUpdate', {
|
|
description: 'Updates the exchange rates from an XML feed',
|
|
accessType: 'WRITE',
|
|
accepts: [],
|
|
http: {
|
|
path: '/exchangeRateUpdate',
|
|
verb: 'post'
|
|
}
|
|
});
|
|
|
|
Self.exchangeRateUpdate = async(options = {}) => {
|
|
const models = Self.app.models;
|
|
const myOptions = {};
|
|
let tx;
|
|
|
|
if (typeof options == 'object')
|
|
Object.assign(myOptions, options);
|
|
|
|
if (!myOptions.transaction) {
|
|
tx = await Self.beginTransaction({});
|
|
myOptions.transaction = tx;
|
|
}
|
|
|
|
try {
|
|
const response = await axios.get('http://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist-90d.xml');
|
|
const xmlData = response.data;
|
|
|
|
const doc = new DOMParser({errorHandler: {warning: () => {}}})
|
|
.parseFromString(xmlData, 'text/xml');
|
|
const cubes = doc?.getElementsByTagName('Cube');
|
|
if (!cubes || cubes.length === 0)
|
|
throw new UserError('No cubes found. Exiting the method.');
|
|
|
|
const currencies = await models.Currency.find({where: {hasToDownloadRate: true}}, myOptions);
|
|
const maxDateRecord = await models.ReferenceRate.findOne({order: 'dated DESC'}, myOptions);
|
|
const maxDate = maxDateRecord?.dated ? new Date(maxDateRecord.dated) : null;
|
|
let lastProcessedDate = maxDate;
|
|
|
|
for (const cube of Array.from(cubes)) {
|
|
if (cube.nodeType === doc.ELEMENT_NODE && cube.attributes.getNamedItem('time')) {
|
|
const xmlDate = new Date(cube.getAttribute('time'));
|
|
const xmlDateWithoutTime = new Date(
|
|
xmlDate.getFullYear(),
|
|
xmlDate.getMonth(),
|
|
xmlDate.getDate()
|
|
);
|
|
|
|
if (!maxDate || xmlDateWithoutTime > maxDate) {
|
|
if (lastProcessedDate && xmlDateWithoutTime > lastProcessedDate) {
|
|
for (const currency of currencies) {
|
|
await fillMissingDates(
|
|
models, currency, lastProcessedDate, xmlDateWithoutTime, myOptions
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (const rateCube of Array.from(cube.childNodes)) {
|
|
if (rateCube.nodeType === doc.ELEMENT_NODE) {
|
|
const currencyCode = rateCube.getAttribute('currency');
|
|
const rate = rateCube.getAttribute('rate');
|
|
const currency = currencies.find(c => c.code === currencyCode);
|
|
if (currency) {
|
|
const existingRate = await models.ReferenceRate.findOne({
|
|
where: {currencyFk: currency.id, dated: xmlDateWithoutTime}
|
|
}, myOptions);
|
|
|
|
if (existingRate) {
|
|
if (existingRate.value !== rate)
|
|
await existingRate.updateAttributes({value: rate}, myOptions);
|
|
} else {
|
|
await models.ReferenceRate.create({
|
|
currencyFk: currency.id,
|
|
dated: xmlDateWithoutTime,
|
|
value: rate
|
|
}, myOptions);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
lastProcessedDate = xmlDateWithoutTime;
|
|
}
|
|
}
|
|
|
|
if (tx) await tx.commit();
|
|
} catch (error) {
|
|
if (tx) await tx.rollback();
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
async function getLastValidRate(models, currencyId, date, myOptions) {
|
|
return models.ReferenceRate.findOne({
|
|
where: {currencyFk: currencyId, dated: {lt: date}},
|
|
order: 'dated DESC'
|
|
}, myOptions);
|
|
}
|
|
|
|
async function fillMissingDates(models, currency, startDate, endDate, myOptions) {
|
|
const cursor = new Date(startDate);
|
|
cursor.setDate(cursor.getDate() + 1);
|
|
while (cursor < endDate) {
|
|
const existingRate = await models.ReferenceRate.findOne({
|
|
where: {currencyFk: currency.id, dated: cursor}
|
|
}, myOptions);
|
|
|
|
if (!existingRate) {
|
|
const lastValid = await getLastValidRate(models, currency.id, cursor, myOptions);
|
|
if (lastValid) {
|
|
await models.ReferenceRate.create({
|
|
currencyFk: currency.id,
|
|
dated: new Date(cursor),
|
|
value: lastValid.value
|
|
}, myOptions);
|
|
}
|
|
}
|
|
cursor.setDate(cursor.getDate() + 1);
|
|
}
|
|
}
|
|
};
|