diff --git a/currency_rate_update_xe/README.rst b/currency_rate_update_xe/README.rst new file mode 100644 index 00000000..e41d5f6a --- /dev/null +++ b/currency_rate_update_xe/README.rst @@ -0,0 +1,99 @@ +============================ +Currency Rate Update: XE.com +============================ + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:54159830b72a5e7058ff6f0040b7d71bdcacbe7f46f2bafe40f5f412ca397ee7 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fcurrency-lightgray.png?logo=github + :target: https://github.com/OCA/currency/tree/16.0/currency_rate_update_xe + :alt: OCA/currency +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/currency-16-0/currency-16-0-currency_rate_update_xe + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/currency&target_branch=16.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module adds `XE.com `_ currency exchange rates provider. + +Disclaimer: The rates are fetched by scraping the corresponding web page. +Being a public page, it's legal according sentences like +https://seointel.com/news/the-court-determines-that-data-scraping-is-legal-on-linkedin/ + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +Now you can choose the 'XE.com' service when configuring +a currency rates providers. + +#. Go to *Invoicing > Configuration > Currency Rates Providers*. +#. Create a new 'Currency Rates Providers' or edit an existing + one and you will see 'XE.com' among the available + 'Source Services' to choose. +#. If you choose 'XE.com' as a 'Source Service', the exchange rates + will be updated from that provider. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Tecnativa + +Contributors +~~~~~~~~~~~~ + +* `Tecnativa `_: + + * Ernesto Tejeda + +* `APSL `_: + + * Miquel Pascual + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/currency `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/currency_rate_update_xe/__init__.py b/currency_rate_update_xe/__init__.py new file mode 100644 index 00000000..4b76c7b2 --- /dev/null +++ b/currency_rate_update_xe/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from . import models diff --git a/currency_rate_update_xe/__manifest__.py b/currency_rate_update_xe/__manifest__.py new file mode 100644 index 00000000..2cb94b9e --- /dev/null +++ b/currency_rate_update_xe/__manifest__.py @@ -0,0 +1,17 @@ +# Copyright 2023 Tecnativa - Ernesto Tejeda +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +{ + "name": "Currency Rate Update: XE.com", + "version": "16.0.1.0.0", + "category": "Financial Management/Configuration", + "summary": "Update exchange rates using XE.com", + "author": "Tecnativa, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/currency", + "license": "AGPL-3", + "installable": True, + "application": False, + "depends": [ + "currency_rate_update", + ], +} diff --git a/currency_rate_update_xe/i18n/currency_rate_update_xe.pot b/currency_rate_update_xe/i18n/currency_rate_update_xe.pot new file mode 100644 index 00000000..0bf51d7d --- /dev/null +++ b/currency_rate_update_xe/i18n/currency_rate_update_xe.pot @@ -0,0 +1,35 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * currency_rate_update_xe +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 15.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: currency_rate_update_xe +#: code:addons/currency_rate_update_xe/models/res_currency_rate_provider_XE.py:0 +#, python-format +msgid "Couldn't fetch data. Please contact your administrator." +msgstr "" + +#. module: currency_rate_update_xe +#: model:ir.model,name:currency_rate_update_xe.model_res_currency_rate_provider +msgid "Currency Rates Provider" +msgstr "" + +#. module: currency_rate_update_xe +#: model:ir.model.fields,field_description:currency_rate_update_xe.field_res_currency_rate_provider__service +msgid "Source Service" +msgstr "" + +#. module: currency_rate_update_xe +#: model:ir.model.fields.selection,name:currency_rate_update_xe.selection__res_currency_rate_provider__service__xe +msgid "XE.com" +msgstr "" diff --git a/currency_rate_update_xe/i18n/es.po b/currency_rate_update_xe/i18n/es.po new file mode 100644 index 00000000..eea50020 --- /dev/null +++ b/currency_rate_update_xe/i18n/es.po @@ -0,0 +1,40 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * currency_rate_update_xe +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 15.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-04-11 07:38+0000\n" +"PO-Revision-Date: 2023-07-05 13:09+0000\n" +"Last-Translator: Ivorra78 \n" +"Language-Team: \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.17\n" + +#. module: currency_rate_update_xe +#: code:addons/currency_rate_update_xe/models/res_currency_rate_provider_XE.py:0 +#, python-format +msgid "Couldn't fetch data. Please contact your administrator." +msgstr "" +"No se pudieron obtener los datos. Por favor, contacte a su administrador." + +#. module: currency_rate_update_xe +#: model:ir.model,name:currency_rate_update_xe.model_res_currency_rate_provider +msgid "Currency Rates Provider" +msgstr "Proveedor de Tipos de Cambio" + +#. module: currency_rate_update_xe +#: model:ir.model.fields,field_description:currency_rate_update_xe.field_res_currency_rate_provider__service +msgid "Source Service" +msgstr "Servicio Fuente" + +#. module: currency_rate_update_xe +#: model:ir.model.fields.selection,name:currency_rate_update_xe.selection__res_currency_rate_provider__service__xe +msgid "XE.com" +msgstr "XE.com" diff --git a/currency_rate_update_xe/models/__init__.py b/currency_rate_update_xe/models/__init__.py new file mode 100644 index 00000000..c974c884 --- /dev/null +++ b/currency_rate_update_xe/models/__init__.py @@ -0,0 +1,2 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from . import res_currency_rate_provider_XE diff --git a/currency_rate_update_xe/models/res_currency_rate_provider_XE.py b/currency_rate_update_xe/models/res_currency_rate_provider_XE.py new file mode 100644 index 00000000..cfdf461b --- /dev/null +++ b/currency_rate_update_xe/models/res_currency_rate_provider_XE.py @@ -0,0 +1,271 @@ +# Copyright 2023 Tecnativa - Ernesto Tejeda +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from datetime import date, timedelta + +import requests +from lxml import etree + +from odoo import _, fields, models +from odoo.exceptions import UserError + + +class ResCurrencyRateProviderXE(models.Model): + _inherit = "res.currency.rate.provider" + + service = fields.Selection( + selection_add=[("XE", "XE.com")], + ondelete={"XE": "set default"}, + ) + + def _get_supported_currencies(self): + self.ensure_one() + if self.service != "XE": + return super()._get_supported_currencies() + # List of currencies obrained from: https://www.xe.com/currency/ + return [ + "USD", + "EUR", + "GBP", + "CAD", + "AUD", + "JPY", + "ADA", + "AED", + "AFN", + "ALL", + "AMD", + "ANG", + "AOA", + "ARS", + "AUD", + "AWG", + "AZN", + "BAM", + "BBD", + "BCH", + "BDT", + "BGN", + "BHD", + "BIF", + "BMD", + "BND", + "BOB", + "BRL", + "BSD", + "BTC", + "BTN", + "BWP", + "BYN", + "BYR", + "BZD", + "CAD", + "CDF", + "CHF", + "CLP", + "CNY", + "COP", + "CRC", + "CUC", + "CUP", + "CVE", + "CZK", + "DJF", + "DKK", + "DOGE", + "DOP", + "DOT", + "DZD", + "EEK", + "EGP", + "ERN", + "ETB", + "ETH", + "EUR", + "FJD", + "FKP", + "GBP", + "GEL", + "GGP", + "GHS", + "GIP", + "GMD", + "GNF", + "GTQ", + "GYD", + "HKD", + "HNL", + "HRK", + "HTG", + "HUF", + "IDR", + "ILS", + "IMP", + "INR", + "IQD", + "IRR", + "ISK", + "JEP", + "JMD", + "JOD", + "JPY", + "KES", + "KGS", + "KHR", + "KMF", + "KPW", + "KRW", + "KWD", + "KYD", + "KZT", + "LAK", + "LBP", + "LINK", + "LKR", + "LRD", + "LSL", + "LTC", + "LTL", + "LUNA", + "LVL", + "LYD", + "MAD", + "MDL", + "MGA", + "MKD", + "MMK", + "MNT", + "MOP", + "MRU", + "MUR", + "MVR", + "MWK", + "MXN", + "MYR", + "MZN", + "NAD", + "NGN", + "NIO", + "NOK", + "NPR", + "NZD", + "OMR", + "PAB", + "PEN", + "PGK", + "PHP", + "PKR", + "PLN", + "PYG", + "QAR", + "RON", + "RSD", + "RUB", + "RWF", + "SAR", + "SBD", + "SCR", + "SDG", + "SEK", + "SGD", + "SHP", + "SLE", + "SLL", + "SOS", + "SPL", + "SRD", + "STN", + "SVC", + "SYP", + "SZL", + "THB", + "TJS", + "TMT", + "TND", + "TOP", + "TRY", + "TTD", + "TVD", + "TWD", + "TZS", + "UAH", + "UGX", + "UNI", + "USD", + "UYU", + "UZS", + "VEF", + "VES", + "VND", + "VUV", + "WST", + "XAF", + "XAG", + "XAU", + "XCD", + "XDR", + "XLM", + "XOF", + "XPD", + "XPF", + "XPT", + "XRP", + "YER", + "ZAR", + "ZMK", + "ZMW", + "ZWD", + ] + + def _obtain_rates(self, base_currency, currencies, date_from, date_to): + self.ensure_one() + if self.service != "XE": + return super()._obtain_rates(base_currency, currencies, date_from, date_to) + base_url = "http://www.xe.com/currencytables" + if date_from < date.today(): + return self._get_historical_rate( + base_url, currencies, date_from, date_to, base_currency + ) + else: + return self._get_latest_rate(base_url, currencies, base_currency) + + def _get_latest_rate(self, base_url, currencies, base_currency): + """Get all the exchange rates for today""" + url = f"{base_url}/?from={base_currency}" + data = self._request_data(url) + return {date.today(): self._parse_data(data, currencies)} + + def _get_historical_rate( + self, base_url, currencies, date_from, date_to, base_currency + ): + """Get all the exchange rates from 'date_from' to 'date_to'""" + content = {} + current_date = date_from + while current_date <= date_to: + url = f"{base_url}/?from={base_currency}&date={current_date.strftime('%Y-%m-%d')}" + data = self._request_data(url) + content[current_date] = self._parse_data(data, currencies) + current_date += timedelta(days=1) + return content + + def _request_data( + self, + url, + ): + try: + return requests.request("GET", url, timeout=10) + except Exception as e: + raise UserError( + _("Couldn't fetch data. Please contact your administrator.") + ) from e + + def _parse_data(self, data, currencies): + result = {} + html_elem = etree.fromstring(data.content, etree.HTMLParser()) + rows_elem = html_elem.xpath(".//div[@id='table-section']//tbody/tr") + for row_elem in rows_elem: + currency_code = "".join(row_elem.find(".//th").itertext()).strip() + if currency_code in currencies: + rate = float(row_elem.find("td[2]").text.replace(",", "")) + result[currency_code] = rate + return result diff --git a/currency_rate_update_xe/readme/CONFIGURE.rst b/currency_rate_update_xe/readme/CONFIGURE.rst new file mode 100644 index 00000000..d235a063 --- /dev/null +++ b/currency_rate_update_xe/readme/CONFIGURE.rst @@ -0,0 +1,9 @@ +Now you can choose the 'XE.com' service when configuring +a currency rates providers. + +#. Go to *Invoicing > Configuration > Currency Rates Providers*. +#. Create a new 'Currency Rates Providers' or edit an existing + one and you will see 'XE.com' among the available + 'Source Services' to choose. +#. If you choose 'XE.com' as a 'Source Service', the exchange rates + will be updated from that provider. diff --git a/currency_rate_update_xe/readme/CONTRIBUTORS.rst b/currency_rate_update_xe/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000..afd154bf --- /dev/null +++ b/currency_rate_update_xe/readme/CONTRIBUTORS.rst @@ -0,0 +1,7 @@ +* `Tecnativa `_: + + * Ernesto Tejeda + +* `APSL `_: + + * Miquel Pascual \ No newline at end of file diff --git a/currency_rate_update_xe/readme/DESCRIPTION.rst b/currency_rate_update_xe/readme/DESCRIPTION.rst new file mode 100644 index 00000000..e53d94d5 --- /dev/null +++ b/currency_rate_update_xe/readme/DESCRIPTION.rst @@ -0,0 +1,5 @@ +This module adds `XE.com `_ currency exchange rates provider. + +Disclaimer: The rates are fetched by scraping the corresponding web page. +Being a public page, it's legal according sentences like +https://seointel.com/news/the-court-determines-that-data-scraping-is-legal-on-linkedin/ diff --git a/currency_rate_update_xe/static/description/icon.png b/currency_rate_update_xe/static/description/icon.png new file mode 100644 index 00000000..3b7d5191 Binary files /dev/null and b/currency_rate_update_xe/static/description/icon.png differ diff --git a/currency_rate_update_xe/static/description/index.html b/currency_rate_update_xe/static/description/index.html new file mode 100644 index 00000000..ecc7b067 --- /dev/null +++ b/currency_rate_update_xe/static/description/index.html @@ -0,0 +1,445 @@ + + + + + + +Currency Rate Update: XE.com + + + +
+

Currency Rate Update: XE.com

+ + +

Beta License: AGPL-3 OCA/currency Translate me on Weblate Try me on Runboat

+

This module adds XE.com currency exchange rates provider.

+

Disclaimer: The rates are fetched by scraping the corresponding web page. +Being a public page, it’s legal according sentences like +https://seointel.com/news/the-court-determines-that-data-scraping-is-legal-on-linkedin/

+

Table of contents

+ +
+

Configuration

+

Now you can choose the ‘XE.com’ service when configuring +a currency rates providers.

+
    +
  1. Go to Invoicing > Configuration > Currency Rates Providers.
  2. +
  3. Create a new ‘Currency Rates Providers’ or edit an existing +one and you will see ‘XE.com’ among the available +‘Source Services’ to choose.
  4. +
  5. If you choose ‘XE.com’ as a ‘Source Service’, the exchange rates +will be updated from that provider.
  6. +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Tecnativa
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/currency project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/currency_rate_update_xe/tests/__init__.py b/currency_rate_update_xe/tests/__init__.py new file mode 100644 index 00000000..10fdd20f --- /dev/null +++ b/currency_rate_update_xe/tests/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from . import test_currency_rate_update_xe diff --git a/currency_rate_update_xe/tests/test_currency_rate_update_xe.py b/currency_rate_update_xe/tests/test_currency_rate_update_xe.py new file mode 100644 index 00000000..8e33fcc2 --- /dev/null +++ b/currency_rate_update_xe/tests/test_currency_rate_update_xe.py @@ -0,0 +1,52 @@ +# Copyright 2023 Tecnativa - Ernesto Tejeda +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + + +from odoo import fields +from odoo.tests import common + + +class TestResCurrencyRateProviderXE(common.TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + + cls.Company = cls.env["res.company"] + cls.CurrencyRate = cls.env["res.currency.rate"] + cls.CurrencyRateProvider = cls.env["res.currency.rate.provider"] + + cls.today = fields.Date.today() + cls.eur_currency = cls.env.ref("base.EUR") + cls.usd_currency = cls.env.ref("base.USD") + cls.company = cls.Company.create( + {"name": "Test company", "currency_id": cls.eur_currency.id} + ) + cls.env.user.company_ids += cls.company + cls.env.company = cls.company + cls.xe_provider = cls.CurrencyRateProvider.create( + { + "service": "XE", + "currency_ids": [ + (4, cls.usd_currency.id), + (4, cls.eur_currency.id), + ], + } + ) + cls.CurrencyRate.search([]).unlink() + + def test_cron(self): + self.xe_provider._scheduled_update() + rates = self.CurrencyRate.search([]) + self.assertEqual(len(rates), 1) + self.assertEqual(rates.currency_id, self.usd_currency) + + def test_wizard(self): + wizard = ( + self.env["res.currency.rate.update.wizard"] + .with_context(default_provider_ids=[(6, False, self.xe_provider.ids)]) + .create({}) + ) + wizard.action_update() + rates = self.CurrencyRate.search([]) + self.assertEqual(len(rates), 1) + self.assertEqual(rates.currency_id, self.usd_currency) diff --git a/setup/currency_rate_update_xe/odoo/addons/currency_rate_update_xe b/setup/currency_rate_update_xe/odoo/addons/currency_rate_update_xe new file mode 120000 index 00000000..d630e185 --- /dev/null +++ b/setup/currency_rate_update_xe/odoo/addons/currency_rate_update_xe @@ -0,0 +1 @@ +../../../../currency_rate_update_xe \ No newline at end of file diff --git a/setup/currency_rate_update_xe/setup.py b/setup/currency_rate_update_xe/setup.py new file mode 100644 index 00000000..28c57bb6 --- /dev/null +++ b/setup/currency_rate_update_xe/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)