From b1ddbeab555eb25a9454463cf84f33a713f6b5b5 Mon Sep 17 00:00:00 2001 From: michelerusti Date: Fri, 7 Feb 2025 15:59:10 +0100 Subject: [PATCH] [ADD] currency_rate_update_fta: Currency Rate Update: Federal Tax Administration --- currency_rate_update_fta/__init__.py | 3 + currency_rate_update_fta/__manifest__.py | 25 ++++++ currency_rate_update_fta/models/__init__.py | 2 + .../models/res_currency_rate_provider_fta.py | 72 ++++++++++++++++++ currency_rate_update_fta/readme/CONFIGURE.rst | 9 +++ .../readme/CONTRIBUTORS.rst | 3 + .../readme/DESCRIPTION.rst | 2 + .../static/description/icon.png | Bin 0 -> 12052 bytes 8 files changed, 116 insertions(+) create mode 100644 currency_rate_update_fta/__init__.py create mode 100644 currency_rate_update_fta/__manifest__.py create mode 100644 currency_rate_update_fta/models/__init__.py create mode 100644 currency_rate_update_fta/models/res_currency_rate_provider_fta.py create mode 100644 currency_rate_update_fta/readme/CONFIGURE.rst create mode 100644 currency_rate_update_fta/readme/CONTRIBUTORS.rst create mode 100644 currency_rate_update_fta/readme/DESCRIPTION.rst create mode 100644 currency_rate_update_fta/static/description/icon.png diff --git a/currency_rate_update_fta/__init__.py b/currency_rate_update_fta/__init__.py new file mode 100644 index 000000000..4b76c7b2d --- /dev/null +++ b/currency_rate_update_fta/__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_fta/__manifest__.py b/currency_rate_update_fta/__manifest__.py new file mode 100644 index 000000000..f12443e09 --- /dev/null +++ b/currency_rate_update_fta/__manifest__.py @@ -0,0 +1,25 @@ +# Copyright 2025 Openindustry +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +{ + "name": "Currency Rate Update: Federal Tax Administration", + "version": "16.0.1.0.0", + "license": "AGPL-3", + "summary": """ + Update exchange rates using https://www.backend-rates.bazg.admin.ch/api/xmldaily + """, + "category": "Financial Management/Configuration", + "company": "https://openindustry.it", + "author": "Openindustry.it,Odoo Community Association (OCA)", + "maintainers": ["andreampiovesana"], + "support": "andrea.m.piovesana@gmail.com", + "website": "https://github.com/OCA/l10n-switzerland", + "depends": [ + "currency_rate_update", + ], + "installable": True, + "application": False, + "auto_install": False, + "price": 10.00, + "currency": "EUR", +} diff --git a/currency_rate_update_fta/models/__init__.py b/currency_rate_update_fta/models/__init__.py new file mode 100644 index 000000000..b66b6cbcf --- /dev/null +++ b/currency_rate_update_fta/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_fta diff --git a/currency_rate_update_fta/models/res_currency_rate_provider_fta.py b/currency_rate_update_fta/models/res_currency_rate_provider_fta.py new file mode 100644 index 000000000..cd71c3104 --- /dev/null +++ b/currency_rate_update_fta/models/res_currency_rate_provider_fta.py @@ -0,0 +1,72 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +import xml.etree.ElementTree as ET +from datetime import date, timedelta + +import requests + +from odoo import _, fields, models +from odoo.exceptions import UserError + + +class ResCurrencyRateProviderFTA(models.Model): + _inherit = "res.currency.rate.provider" + + service = fields.Selection( + selection_add=[("fta", "Federal Tax Administration (Switzerland)")], + ondelete={"fta": "set default"}, + ) + + def _obtain_rates(self, base_currency, currencies, date_from, date_to): + self.ensure_one() + if self.service != "fta": + return super()._obtain_rates(base_currency, currencies, date_from, date_to) + base_url = "https://www.backend-rates.bazg.admin.ch/api/xmldaily" + 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}" + 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}/?d={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 = {} + root = ET.fromstring(data.content) + namespace = {"ns": "https://www.backend-rates.ezv.admin.ch/xmldaily"} + for devise in root.findall("ns:devise", namespace): + currency_code = devise.get("code").upper() + if currency_code in currencies: + rate = devise.find("ns:kurs", namespace).text + div = devise.find("ns:waehrung", namespace).text + div_list = div.split(" ") + divisor = int(div_list[0]) + rate_float = float(rate) + result[currency_code] = str(rate_float / divisor) + return result diff --git a/currency_rate_update_fta/readme/CONFIGURE.rst b/currency_rate_update_fta/readme/CONFIGURE.rst new file mode 100644 index 000000000..a94b21cf2 --- /dev/null +++ b/currency_rate_update_fta/readme/CONFIGURE.rst @@ -0,0 +1,9 @@ +Now you can choose the swiss 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 Federal Tax Administration among the available + 'Source Services' to choose. +#. If you choose Federal Tax Administration as a 'Source Service', the exchange rates + will be updated from that provider. diff --git a/currency_rate_update_fta/readme/CONTRIBUTORS.rst b/currency_rate_update_fta/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..95f3c113f --- /dev/null +++ b/currency_rate_update_fta/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* `Openindustry `_: + + * Andrea Piovesana diff --git a/currency_rate_update_fta/readme/DESCRIPTION.rst b/currency_rate_update_fta/readme/DESCRIPTION.rst new file mode 100644 index 000000000..01c4b2ace --- /dev/null +++ b/currency_rate_update_fta/readme/DESCRIPTION.rst @@ -0,0 +1,2 @@ +This module adds Federal Tax Administration currency exchange rates provider. +https://www.backend-rates.bazg.admin.ch/api/xmldaily \ No newline at end of file diff --git a/currency_rate_update_fta/static/description/icon.png b/currency_rate_update_fta/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..6791aee1af92dc20024053f274cb5d0284d94f50 GIT binary patch literal 12052 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 R5M1Vtpd_y*S1V%?{y%reK+6CC literal 0 HcmV?d00001