From 13ffd40d2cdbf20e80255da4bff9321535c59dca Mon Sep 17 00:00:00 2001 From: Lam Thai Binh Date: Fri, 16 Jun 2017 18:09:36 +0700 Subject: [PATCH 1/4] Add service update VCB exchange rates --- currency_rate_update/README.rst | 1 + .../services/update_service_VCB.py | 88 +++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 currency_rate_update/services/update_service_VCB.py diff --git a/currency_rate_update/README.rst b/currency_rate_update/README.rst index 5e6972ed0..c0ec4fc8c 100644 --- a/currency_rate_update/README.rst +++ b/currency_rate_update/README.rst @@ -105,6 +105,7 @@ Contributors * Assem Bayahi * Daniel Dico (BOC) * Dmytro Katyukha +* Lam Thai Binh (VCB) Maintainer ---------- diff --git a/currency_rate_update/services/update_service_VCB.py b/currency_rate_update/services/update_service_VCB.py new file mode 100644 index 000000000..6546d7c18 --- /dev/null +++ b/currency_rate_update/services/update_service_VCB.py @@ -0,0 +1,88 @@ +# -*- coding: utf-8 -*- + +from datetime import datetime +import logging + +from lxml import etree + +from .currency_getter_interface import CurrencyGetterInterface + +_logger = logging.getLogger(__name__) + + +class VCBGetter(CurrencyGetterInterface): + """Implementation of Currency_getter_factory interface + for VCB service + """ + code = 'VCB' + name = 'Joint Stock Commercial Bank for Foreign ' \ + 'Trade of Vietnam - Vietcombank' + supported_currency_array = [ + "AUD", "CAD", "CHF", "DKK", "EUR", "GBP", "HKD", "INR", "JPY", "KRW", + "KWD", "MYR", "NOK", "RUB", "SAR", "SEK", "SGD", "THB", "USD", "VND"] + + def rate_retrieve(self, dom, ns, curr): + """Parse a dom node to retrieve- + currencies data + """ + res = {} + xpath_curr_rate = ("/ExrateList/Exrate[@CurrencyCode='%s']/@Transfer" + % curr.upper()) + + res['rate_currency'] = float( + dom.xpath(xpath_curr_rate, namespaces=ns)[0] + ) + return res + + def get_updated_currency(self, currency_array, main_currency, + max_delta_days): + + """implementation of abstract method of Curreny_getter_interface""" + url = 'http://www.vietcombank.com.vn/ExchangeRates/ExrateXML.aspx' + # we do not want to update the main currency + if main_currency in currency_array: + currency_array.remove(main_currency) + _logger.debug("VCB currency rate service : connecting...") + + rawfile = self.get_url(url) + dom = etree.fromstring(rawfile) + vcb_ns = {} # there are no namespace ! + _logger.debug("VCB sent a valid XML file") + rate_date = dom.xpath('/ExrateList/DateTime/text()', + namespaces=vcb_ns)[0] + # Don't use DEFAULT_SERVER_DATE_FORMAT here, because it's + # the format of the XML of VCB, not the format of Odoo server ! + rate_date_datetime = datetime.strptime( + str(rate_date), '%m/%d/%Y %I:%M:%S %p') + + self.check_rate_date(rate_date_datetime, max_delta_days) + # We dynamically update supported currencies + self.supported_currency_array = dom.xpath( + "/ExrateList/Exrate/@CurrencyCode", + namespaces=vcb_ns) + + self.supported_currency_array.append('VND') + _logger.debug("Supported currencies = %s " % + self.supported_currency_array) + + self.validate_cur(main_currency) + if main_currency != 'VND': + main_curr_data = self.rate_retrieve(dom, vcb_ns, main_currency) + + for curr in currency_array: + self.validate_cur(curr) + if curr == 'VND': + rate = 1 / main_curr_data['rate_currency'] + else: + curr_data = self.rate_retrieve(dom, vcb_ns, curr) + if main_currency == 'VND': + rate = curr_data['rate_currency'] + else: + rate = (curr_data['rate_currency'] / + main_curr_data['rate_currency']) + + self.updated_currency[curr] = rate + _logger.debug( + "Rate retrieved : 1 %s = %s %s" % (main_currency, rate, curr)) + + return self.updated_currency, self.log_info From 8e54668aaa0ea31c118376e92dfba49c5ecc9ab9 Mon Sep 17 00:00:00 2001 From: Jean-Charles Drubay Date: Sat, 24 Jun 2017 09:46:48 +0700 Subject: [PATCH 2/4] Minor adjustments after tests: - Add Vietcombank to README.md - Don't invert exchange rate - Fix missing import --- currency_rate_update/README.rst | 6 +- .../models/currency_rate_update.py | 2 +- currency_rate_update/services/__init__.py | 1 + .../services/update_service_CA_BOC.py | 2 +- .../services/update_service_CH_ADMIN.py | 2 +- .../services/update_service_ECB.py | 2 +- .../services/update_service_MX_BdM.py | 2 +- .../services/update_service_PL_NBP.py | 2 +- .../services/update_service_RO_BNR.py | 2 +- ...ervice_VCB.py => update_service_VN_VCB.py} | 28 +++--- .../services/update_service_YAHOO.py | 2 +- currency_rate_update/tests/__init__.py | 5 ++ .../tests/test_currency_rate_update.py | 86 +++++++++++++++++++ 13 files changed, 118 insertions(+), 24 deletions(-) rename currency_rate_update/services/{update_service_VCB.py => update_service_VN_VCB.py} (81%) create mode 100644 currency_rate_update/tests/__init__.py create mode 100644 currency_rate_update/tests/test_currency_rate_update.py diff --git a/currency_rate_update/README.rst b/currency_rate_update/README.rst index c0ec4fc8c..51dda11f9 100644 --- a/currency_rate_update/README.rst +++ b/currency_rate_update/README.rst @@ -37,6 +37,9 @@ The module is able to use the following sources: 7. National Bank of Romania (Banca Nationala a Romaniei) +8. Joint Stock Commercial Bank for Foreign Trade of Vietnam - Vietcombank + + Configuration ============= @@ -69,8 +72,7 @@ To fix: Roadmap: * Google Finance. -* Updated daily from Citibank N.A., source in EUR. Information may be delayed. - This is parsed from an HTML page, so it may be broken at anytime. +* Updated daily from Citibank N.A., source in EUR. Information may be delayed. This is parsed from an HTML page, so it may be broken at anytime. Bug Tracker =========== diff --git a/currency_rate_update/models/currency_rate_update.py b/currency_rate_update/models/currency_rate_update.py index 0a825fd39..d50533e3a 100644 --- a/currency_rate_update/models/currency_rate_update.py +++ b/currency_rate_update/models/currency_rate_update.py @@ -106,7 +106,7 @@ class CurrencyRateUpdateService(models.Model): # Note fileds that will be used as a logger note = fields.Text('Update logs') max_delta_days = fields.Integer( - string='Max delta days', default=4, required=True, + default=4, required=True, help="If the time delta between the rate date given by the " "webservice and the current date exceeds this value, " "then the currency rate is not updated in Odoo.") diff --git a/currency_rate_update/services/__init__.py b/currency_rate_update/services/__init__.py index e08882d2e..1afccd016 100644 --- a/currency_rate_update/services/__init__.py +++ b/currency_rate_update/services/__init__.py @@ -6,3 +6,4 @@ from . import update_service_YAHOO from . import update_service_PL_NBP from . import update_service_MX_BdM from . import update_service_RO_BNR +from . import update_service_VN_VCB diff --git a/currency_rate_update/services/update_service_CA_BOC.py b/currency_rate_update/services/update_service_CA_BOC.py index 853adfeab..c3a4dea1b 100644 --- a/currency_rate_update/services/update_service_CA_BOC.py +++ b/currency_rate_update/services/update_service_CA_BOC.py @@ -12,7 +12,7 @@ import logging _logger = logging.getLogger(__name__) -class CA_BOCGetter(CurrencyGetterInterface): +class CaBocGetter(CurrencyGetterInterface): """Implementation of Curreny_getter_factory interface for Bank of Canada RSS service diff --git a/currency_rate_update/services/update_service_CH_ADMIN.py b/currency_rate_update/services/update_service_CH_ADMIN.py index 93e329db8..42ce8c88c 100644 --- a/currency_rate_update/services/update_service_CH_ADMIN.py +++ b/currency_rate_update/services/update_service_CH_ADMIN.py @@ -10,7 +10,7 @@ from odoo.tools import DEFAULT_SERVER_DATE_FORMAT _logger = logging.getLogger(__name__) -class CH_ADMINGetter(CurrencyGetterInterface): +class ChAdminGetter(CurrencyGetterInterface): """Implementation of Currency_getter_factory interface for Admin.ch service. """ diff --git a/currency_rate_update/services/update_service_ECB.py b/currency_rate_update/services/update_service_ECB.py index 73ea0689d..04b4e920a 100644 --- a/currency_rate_update/services/update_service_ECB.py +++ b/currency_rate_update/services/update_service_ECB.py @@ -12,7 +12,7 @@ import logging _logger = logging.getLogger(__name__) -class ECBGetter(CurrencyGetterInterface): +class EcbGetter(CurrencyGetterInterface): """Implementation of Currency_getter_factory interface for ECB service """ diff --git a/currency_rate_update/services/update_service_MX_BdM.py b/currency_rate_update/services/update_service_MX_BdM.py index 4d0bc30f0..591c50ce3 100644 --- a/currency_rate_update/services/update_service_MX_BdM.py +++ b/currency_rate_update/services/update_service_MX_BdM.py @@ -9,7 +9,7 @@ import logging _logger = logging.getLogger(__name__) -class MX_BdMGetter(CurrencyGetterInterface): +class MxBdmGetter(CurrencyGetterInterface): """Implementation of Currency_getter_factory interface for Banco de México service diff --git a/currency_rate_update/services/update_service_PL_NBP.py b/currency_rate_update/services/update_service_PL_NBP.py index aabf15609..5f02715ce 100644 --- a/currency_rate_update/services/update_service_PL_NBP.py +++ b/currency_rate_update/services/update_service_PL_NBP.py @@ -12,7 +12,7 @@ import logging _logger = logging.getLogger(__name__) -class PL_NBPGetter(CurrencyGetterInterface): +class PlNbpGetter(CurrencyGetterInterface): """Implementation of Currency_getter_factory interface for PL NBP service diff --git a/currency_rate_update/services/update_service_RO_BNR.py b/currency_rate_update/services/update_service_RO_BNR.py index 650bcb85f..4484dc5c7 100644 --- a/currency_rate_update/services/update_service_RO_BNR.py +++ b/currency_rate_update/services/update_service_RO_BNR.py @@ -11,7 +11,7 @@ import logging _logger = logging.getLogger(__name__) -class RO_BNRGetter(CurrencyGetterInterface): +class RoBnrGetter(CurrencyGetterInterface): """Implementation of Currency_getter_factory interface for BNR service""" code = 'RO_BNR' diff --git a/currency_rate_update/services/update_service_VCB.py b/currency_rate_update/services/update_service_VN_VCB.py similarity index 81% rename from currency_rate_update/services/update_service_VCB.py rename to currency_rate_update/services/update_service_VN_VCB.py index 6546d7c18..dc8679280 100644 --- a/currency_rate_update/services/update_service_VCB.py +++ b/currency_rate_update/services/update_service_VN_VCB.py @@ -1,4 +1,7 @@ # -*- coding: utf-8 -*- +# © 2017 Binh Lam +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + from datetime import datetime import logging @@ -10,11 +13,10 @@ from .currency_getter_interface import CurrencyGetterInterface _logger = logging.getLogger(__name__) -class VCBGetter(CurrencyGetterInterface): - """Implementation of Currency_getter_factory interface - for VCB service - """ - code = 'VCB' +class VnVcbGetter(CurrencyGetterInterface): + """Implementation of Currency_getter_factory interface for VCB service.""" + + code = 'VN_VCB' name = 'Joint Stock Commercial Bank for Foreign ' \ 'Trade of Vietnam - Vietcombank' supported_currency_array = [ @@ -22,9 +24,7 @@ class VCBGetter(CurrencyGetterInterface): "KWD", "MYR", "NOK", "RUB", "SAR", "SEK", "SGD", "THB", "USD", "VND"] def rate_retrieve(self, dom, ns, curr): - """Parse a dom node to retrieve- - currencies data - """ + """Parse a dom node to retrieve-currencies data.""" res = {} xpath_curr_rate = ("/ExrateList/Exrate[@CurrencyCode='%s']/@Transfer" % curr.upper()) @@ -36,8 +36,7 @@ class VCBGetter(CurrencyGetterInterface): def get_updated_currency(self, currency_array, main_currency, max_delta_days): - - """implementation of abstract method of Curreny_getter_interface""" + """Implementation of abstract method of Curreny_getter_interface.""" url = 'http://www.vietcombank.com.vn/ExchangeRates/ExrateXML.aspx' # we do not want to update the main currency if main_currency in currency_array: @@ -72,14 +71,15 @@ class VCBGetter(CurrencyGetterInterface): for curr in currency_array: self.validate_cur(curr) if curr == 'VND': - rate = 1 / main_curr_data['rate_currency'] + rate = main_curr_data['rate_currency'] else: curr_data = self.rate_retrieve(dom, vcb_ns, curr) if main_currency == 'VND': - rate = curr_data['rate_currency'] + rate = 1 / curr_data['rate_currency'] else: - rate = (curr_data['rate_currency'] / - main_curr_data['rate_currency']) + rate = ( + main_curr_data['rate_currency'] / + curr_data['rate_currency']) self.updated_currency[curr] = rate _logger.debug( diff --git a/currency_rate_update/services/update_service_YAHOO.py b/currency_rate_update/services/update_service_YAHOO.py index 1ecde3ebe..5c0520a0b 100644 --- a/currency_rate_update/services/update_service_YAHOO.py +++ b/currency_rate_update/services/update_service_YAHOO.py @@ -5,7 +5,7 @@ from .currency_getter_interface import CurrencyGetterInterface -class YAHOOGetter(CurrencyGetterInterface): +class YahooGetter(CurrencyGetterInterface): """Implementation of Currency_getter_factory interface for Yahoo finance service """ diff --git a/currency_rate_update/tests/__init__.py b/currency_rate_update/tests/__init__.py new file mode 100644 index 000000000..433c9d382 --- /dev/null +++ b/currency_rate_update/tests/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Author: Lam Thai Binh +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import test_currency_rate_update diff --git a/currency_rate_update/tests/test_currency_rate_update.py b/currency_rate_update/tests/test_currency_rate_update.py new file mode 100644 index 000000000..9b412cbd3 --- /dev/null +++ b/currency_rate_update/tests/test_currency_rate_update.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- +# Author: Lam Thai Binh +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import datetime +import logging + +from odoo import fields +from odoo.tests.common import TransactionCase + + +class TestCurrencyRateUpdate(TransactionCase): + + def setUp(self): + super(TestCurrencyRateUpdate, self).setUp() + self.env.user.company_id.auto_currency_up = True + self.env.user.company_id.currency_id = self.env.ref('base.EUR') + self.service_env = self.env['currency.rate.update.service'] + self.rate_env = self.env['res.currency.rate'] + + def _test_cron_by_service(self, service_code, currency_xml_ids): + """Test the ir.cron with any service for some currencies + """ + currency_ids = [ + self.env.ref(currency_xml_id).id + for currency_xml_id in currency_xml_ids] + + service_x = self.service_env.create({ + 'service': service_code, + 'currency_to_update': [(6, 0, currency_ids)] + }) + + rate_name = \ + fields.Datetime.to_string(datetime.datetime.utcnow().replace( + hour=0, minute=0, second=0, microsecond=0)) + for currency_id in currency_ids: + rates = self.rate_env.search([ + ('currency_id', '=', currency_id), + ('company_id', '=', self.env.user.company_id.id), + ('name', '=', rate_name)]) + rates.unlink() + self.service_env._run_currency_update() + logging.info("Service note: %s", service_x.note) + self.assertFalse('ERROR' in service_x.note) + + # FIXME: except_orm(u'Error !', u'Exchange data for USD is not reported + # by Bank of Canada.') + # def test_cron_CA_BOC(self): + # """Test the ir.cron with Bank of Canada service for USD + # """ + # self._test_cron_by_service('CA_BOC', ['base.USD']) + + def test_cron_CH_ADMIN(self): + """Test the ir.cron with Admin.ch service for USD + """ + self._test_cron_by_service('CH_ADMIN', ['base.USD']) + + def test_cron_ECB(self): + """Test the ir.cron with European Central Bank service for USD + """ + self._test_cron_by_service('ECB', ['base.USD']) + + def test_cron_MX_BdM(self): + """Test the ir.cron with Bank of Mexico service for USD + """ + self._test_cron_by_service('MX_BdM', ['base.USD']) + + def test_cron_PL_NBP(self): + """Test the ir.cron with National Bank of Poland service for USD + """ + self._test_cron_by_service('PL_NBP', ['base.USD']) + + def test_cron_RO_BNR(self): + """Test the ir.cron with National Bank of Romania service for USD + """ + self._test_cron_by_service('RO_BNR', ['base.USD']) + + def test_cron_VN_VCB(self): + """Test the ir.cron with Vietcombank service for USD + """ + self._test_cron_by_service('VN_VCB', ['base.USD']) + + def test_cron_YAHOO(self): + """Test the ir.cron with Yahoo service for USD + """ + self._test_cron_by_service('YAHOO', ['base.USD']) From dfa249919a977576d79bd261a8c748a204113eef Mon Sep 17 00:00:00 2001 From: Lam Thai Binh Date: Thu, 23 Nov 2017 17:12:56 +0700 Subject: [PATCH 3/4] Remove test case for Yahoo service --- currency_rate_update/tests/test_currency_rate_update.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/currency_rate_update/tests/test_currency_rate_update.py b/currency_rate_update/tests/test_currency_rate_update.py index 9b412cbd3..563e642c5 100644 --- a/currency_rate_update/tests/test_currency_rate_update.py +++ b/currency_rate_update/tests/test_currency_rate_update.py @@ -80,7 +80,3 @@ class TestCurrencyRateUpdate(TransactionCase): """ self._test_cron_by_service('VN_VCB', ['base.USD']) - def test_cron_YAHOO(self): - """Test the ir.cron with Yahoo service for USD - """ - self._test_cron_by_service('YAHOO', ['base.USD']) From 837703b8106991c1fd2704ee53ed85ac2f4598f7 Mon Sep 17 00:00:00 2001 From: Lam Thai Binh Date: Thu, 23 Nov 2017 17:24:00 +0700 Subject: [PATCH 4/4] Remove blank line at the end of file. --- currency_rate_update/tests/test_currency_rate_update.py | 1 - 1 file changed, 1 deletion(-) diff --git a/currency_rate_update/tests/test_currency_rate_update.py b/currency_rate_update/tests/test_currency_rate_update.py index 563e642c5..6b351edf9 100644 --- a/currency_rate_update/tests/test_currency_rate_update.py +++ b/currency_rate_update/tests/test_currency_rate_update.py @@ -79,4 +79,3 @@ class TestCurrencyRateUpdate(TransactionCase): """Test the ir.cron with Vietcombank service for USD """ self._test_cron_by_service('VN_VCB', ['base.USD']) -