From 2024e3d5458553d5b620b1d3b76dd94bfc97e779 Mon Sep 17 00:00:00 2001 From: Fekete Mihai Date: Mon, 10 Nov 2014 12:11:10 +0200 Subject: [PATCH 01/32] Move directory to main folder. --- .../__init__.py | 0 .../__openerp__.py | 0 .../company.py | 0 .../company_view.xml | 0 .../currency_rate_update.py | 0 .../currency_rate_update.xml | 0 .../i18n/ab.po | 0 .../i18n/currency_rate_update.pot | 0 .../i18n/es.po | 0 .../i18n/fr_FR.po | 0 .../i18n/pl.po | 0 .../i18n/pl_PL.po | 0 .../i18n/pt_BR.po | 0 .../security/security.xml | 0 .../static/src/img/icon.png | Bin 15 files changed, 0 insertions(+), 0 deletions(-) rename {__unported__/currency_rate_update => currency_rate_update}/__init__.py (100%) rename {__unported__/currency_rate_update => currency_rate_update}/__openerp__.py (100%) rename {__unported__/currency_rate_update => currency_rate_update}/company.py (100%) rename {__unported__/currency_rate_update => currency_rate_update}/company_view.xml (100%) rename {__unported__/currency_rate_update => currency_rate_update}/currency_rate_update.py (100%) rename {__unported__/currency_rate_update => currency_rate_update}/currency_rate_update.xml (100%) rename {__unported__/currency_rate_update => currency_rate_update}/i18n/ab.po (100%) rename {__unported__/currency_rate_update => currency_rate_update}/i18n/currency_rate_update.pot (100%) rename {__unported__/currency_rate_update => currency_rate_update}/i18n/es.po (100%) rename {__unported__/currency_rate_update => currency_rate_update}/i18n/fr_FR.po (100%) rename {__unported__/currency_rate_update => currency_rate_update}/i18n/pl.po (100%) rename {__unported__/currency_rate_update => currency_rate_update}/i18n/pl_PL.po (100%) rename {__unported__/currency_rate_update => currency_rate_update}/i18n/pt_BR.po (100%) rename {__unported__/currency_rate_update => currency_rate_update}/security/security.xml (100%) rename {__unported__/currency_rate_update => currency_rate_update}/static/src/img/icon.png (100%) diff --git a/__unported__/currency_rate_update/__init__.py b/currency_rate_update/__init__.py similarity index 100% rename from __unported__/currency_rate_update/__init__.py rename to currency_rate_update/__init__.py diff --git a/__unported__/currency_rate_update/__openerp__.py b/currency_rate_update/__openerp__.py similarity index 100% rename from __unported__/currency_rate_update/__openerp__.py rename to currency_rate_update/__openerp__.py diff --git a/__unported__/currency_rate_update/company.py b/currency_rate_update/company.py similarity index 100% rename from __unported__/currency_rate_update/company.py rename to currency_rate_update/company.py diff --git a/__unported__/currency_rate_update/company_view.xml b/currency_rate_update/company_view.xml similarity index 100% rename from __unported__/currency_rate_update/company_view.xml rename to currency_rate_update/company_view.xml diff --git a/__unported__/currency_rate_update/currency_rate_update.py b/currency_rate_update/currency_rate_update.py similarity index 100% rename from __unported__/currency_rate_update/currency_rate_update.py rename to currency_rate_update/currency_rate_update.py diff --git a/__unported__/currency_rate_update/currency_rate_update.xml b/currency_rate_update/currency_rate_update.xml similarity index 100% rename from __unported__/currency_rate_update/currency_rate_update.xml rename to currency_rate_update/currency_rate_update.xml diff --git a/__unported__/currency_rate_update/i18n/ab.po b/currency_rate_update/i18n/ab.po similarity index 100% rename from __unported__/currency_rate_update/i18n/ab.po rename to currency_rate_update/i18n/ab.po diff --git a/__unported__/currency_rate_update/i18n/currency_rate_update.pot b/currency_rate_update/i18n/currency_rate_update.pot similarity index 100% rename from __unported__/currency_rate_update/i18n/currency_rate_update.pot rename to currency_rate_update/i18n/currency_rate_update.pot diff --git a/__unported__/currency_rate_update/i18n/es.po b/currency_rate_update/i18n/es.po similarity index 100% rename from __unported__/currency_rate_update/i18n/es.po rename to currency_rate_update/i18n/es.po diff --git a/__unported__/currency_rate_update/i18n/fr_FR.po b/currency_rate_update/i18n/fr_FR.po similarity index 100% rename from __unported__/currency_rate_update/i18n/fr_FR.po rename to currency_rate_update/i18n/fr_FR.po diff --git a/__unported__/currency_rate_update/i18n/pl.po b/currency_rate_update/i18n/pl.po similarity index 100% rename from __unported__/currency_rate_update/i18n/pl.po rename to currency_rate_update/i18n/pl.po diff --git a/__unported__/currency_rate_update/i18n/pl_PL.po b/currency_rate_update/i18n/pl_PL.po similarity index 100% rename from __unported__/currency_rate_update/i18n/pl_PL.po rename to currency_rate_update/i18n/pl_PL.po diff --git a/__unported__/currency_rate_update/i18n/pt_BR.po b/currency_rate_update/i18n/pt_BR.po similarity index 100% rename from __unported__/currency_rate_update/i18n/pt_BR.po rename to currency_rate_update/i18n/pt_BR.po diff --git a/__unported__/currency_rate_update/security/security.xml b/currency_rate_update/security/security.xml similarity index 100% rename from __unported__/currency_rate_update/security/security.xml rename to currency_rate_update/security/security.xml diff --git a/__unported__/currency_rate_update/static/src/img/icon.png b/currency_rate_update/static/src/img/icon.png similarity index 100% rename from __unported__/currency_rate_update/static/src/img/icon.png rename to currency_rate_update/static/src/img/icon.png From 1c43c79e56147c5e768a3ffff1a225b78556c7ee Mon Sep 17 00:00:00 2001 From: Fekete Mihai Date: Mon, 10 Nov 2014 12:30:10 +0200 Subject: [PATCH 02/32] Make the module instalable. --- currency_rate_update/__openerp__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/currency_rate_update/__openerp__.py b/currency_rate_update/__openerp__.py index 6d7ba3bf9..1294a3f80 100644 --- a/currency_rate_update/__openerp__.py +++ b/currency_rate_update/__openerp__.py @@ -93,5 +93,5 @@ Thanks to main contributors: Grzegorz Grzelak, Alexis de Lattre ], "demo": [], "active": False, - 'installable': False + 'installable': True } From 0f61f5efe14574c97e679d205af01f76725f1686 Mon Sep 17 00:00:00 2001 From: Fekete Mihai Date: Mon, 10 Nov 2014 12:32:47 +0200 Subject: [PATCH 03/32] Move files to subdirectories. --- currency_rate_update/{ => model}/company.py | 0 currency_rate_update/{ => model}/currency_rate_update.py | 0 currency_rate_update/{ => view}/company_view.xml | 0 currency_rate_update/{ => view}/currency_rate_update.xml | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename currency_rate_update/{ => model}/company.py (100%) rename currency_rate_update/{ => model}/currency_rate_update.py (100%) rename currency_rate_update/{ => view}/company_view.xml (100%) rename currency_rate_update/{ => view}/currency_rate_update.xml (100%) diff --git a/currency_rate_update/company.py b/currency_rate_update/model/company.py similarity index 100% rename from currency_rate_update/company.py rename to currency_rate_update/model/company.py diff --git a/currency_rate_update/currency_rate_update.py b/currency_rate_update/model/currency_rate_update.py similarity index 100% rename from currency_rate_update/currency_rate_update.py rename to currency_rate_update/model/currency_rate_update.py diff --git a/currency_rate_update/company_view.xml b/currency_rate_update/view/company_view.xml similarity index 100% rename from currency_rate_update/company_view.xml rename to currency_rate_update/view/company_view.xml diff --git a/currency_rate_update/currency_rate_update.xml b/currency_rate_update/view/currency_rate_update.xml similarity index 100% rename from currency_rate_update/currency_rate_update.xml rename to currency_rate_update/view/currency_rate_update.xml From 151e0d536e57e7015352a18e04e1100f8b0230bd Mon Sep 17 00:00:00 2001 From: Fekete Mihai Date: Mon, 10 Nov 2014 12:34:43 +0200 Subject: [PATCH 04/32] Update company to new api. --- currency_rate_update/model/company.py | 164 ++++++-------------------- 1 file changed, 33 insertions(+), 131 deletions(-) diff --git a/currency_rate_update/model/company.py b/currency_rate_update/model/company.py index 8c8b66feb..a5f7033af 100644 --- a/currency_rate_update/model/company.py +++ b/currency_rate_update/model/company.py @@ -18,142 +18,44 @@ # along with this program. If not, see . # ############################################################################## -from openerp.osv import fields, orm + +from openerp import models, fields, api, _ -class res_company(orm.Model): +class res_company(models.Model): """override company to add currency update""" - def _multi_curr_enable(self, cr, uid, ids, field_name, arg, context=None): + @api.multi + def _compute_multi_curr_enable(self): "check if multi company currency is enabled" - result = {} - field_ids = self.pool.get('ir.model.fields').search( - cr, uid, - [('name', '=', 'company_id'), - ('model', '=', 'res.currency')] - ) - if not field_ids: - enable = 0 - else: - enable = 1 - for id in ids: - result[id] = enable - return result + company_currency = self.env['res.currency'].search([('company_id', + '!=', False)]) + for company in self: + company.multi_company_currency_enable = \ + 1 if company_currency else 0 - def button_refresh_currency(self, cr, uid, ids, context=None): - """Refrech the currency for all the company now""" - currency_updater_obj = self.pool.get('currency.rate.update') - try: - currency_updater_obj.run_currency_update(cr, uid) - except Exception: - return False - return True - - def on_change_auto_currency_up(self, cr, uid, id, value): - """handle the activation of the currecny update on compagnies. - There are two ways of implementing multi_company currency, - the currency is shared or not. The module take care of the two - ways. If the currency are shared, you will only be able to set - auto update on one company, this will avoid to have unusefull cron - object running. - If yours currency are not share you will be able to activate the - auto update on each separated company - - """ - - if len(id): - id = id[0] - else: - return {} - enable = self.browse(cr, uid, id).multi_company_currency_enable - compagnies = self.search(cr, uid, []) - activate_cron = 'f' - if not value: - # this statement is here beacaus we do no want to save # - # in case of error - self.write(cr, uid, id, {'auto_currency_up': value}) - for comp in compagnies: - if self.browse(cr, uid, comp).auto_currency_up: - activate_cron = 't' - break - self.pool.get('currency.rate.update').save_cron( - cr, - uid, - {'active': activate_cron} - ) - return {} - else: - for comp in compagnies: - if comp != id and not enable: - current = self.browse(cr, uid, comp) - if current.multi_company_currency_enable: - # We ensure taht we did not have write a true value - self.write(cr, uid, id, {'auto_currency_up': False}) - msg = ('You can not activate auto currency' - 'update on more thant one company with this ' - 'multi company configuration') - return { - 'value': {'auto_currency_up': False}, - - 'warning': { - 'title': "Warning", - 'message': msg, - } - } - self.write(cr, uid, id, {'auto_currency_up': value}) - for comp in compagnies: - if self.browse(cr, uid, comp).auto_currency_up: - activate_cron = 't' - self.pool.get('currency.rate.update').save_cron( - cr, - uid, - {'active': activate_cron} - ) - break - return {} - - def on_change_intervall(self, cr, uid, id, interval): - # Function that will update the cron freqeuence - self.pool.get('currency.rate.update').save_cron( - cr, - uid, - {'interval_type': interval} - ) - compagnies = self.search(cr, uid, []) - for comp in compagnies: - self.write(cr, uid, comp, {'interval_type': interval}) - return {} + @api.one + def button_refresh_currency(self): + """Refresh the currencies rates !!for all companies now""" + self.services_to_use.refresh_currency() _inherit = "res.company" - _columns = { - # Activate the currency update - 'auto_currency_up': fields.boolean( - 'Automatical update of the currency this company' - ), - 'services_to_use': fields.one2many( - 'currency.rate.update.service', - 'company_id', - 'Currency update services' - ), - # Predifine cron frequence - 'interval_type': fields.selection( - [ - ('days', 'Day(s)'), - ('weeks', 'Week(s)'), - ('months', 'Month(s)') - ], - 'Currency update frequence', - help="Changing this value will " - "also affect other compagnies" - ), - # Function field that allows to know the - # mutli company currency implementation - 'multi_company_currency_enable': fields.function( - _multi_curr_enable, - method=True, - type='boolean', - string="Multi company currency", - help="If this case is not check you can" - " not set currency is active on two company" - ), - } + _name = "res.company" + + # Activate the currency update + auto_currency_up = fields.Boolean( + string="Automatic Update", + help="Automatical update of the currencies for this company") + # Function field that allows to know the + # multi company currency implementation + multi_company_currency_enable = fields.Boolean( + string="Multi company currency", + compute="_compute_multi_curr_enable", + help="When this option is unchecked it will allow users " + "to set a distinct currency updates on each company." + ) + # List of services to fetch rates + services_to_use = fields.One2many( + 'currency.rate.update.service', + 'company_id', + 'Currency update services') From c73b2bc4e963400fae126a7a40fe14a540e5f037 Mon Sep 17 00:00:00 2001 From: Fekete Mihai Date: Mon, 10 Nov 2014 12:37:45 +0200 Subject: [PATCH 05/32] Update res currency update services to new api. --- .../model/currency_rate_update.py | 1016 +++++------------ .../services/currency_rate_update.py | 799 +++++++++++++ 2 files changed, 1059 insertions(+), 756 deletions(-) create mode 100644 currency_rate_update/services/currency_rate_update.py diff --git a/currency_rate_update/model/currency_rate_update.py b/currency_rate_update/model/currency_rate_update.py index 91419dfdc..e49677161 100644 --- a/currency_rate_update/model/currency_rate_update.py +++ b/currency_rate_update/model/currency_rate_update.py @@ -1,22 +1,8 @@ # -*- coding: utf-8 -*- ############################################################################## # -# Copyright (c) 2009 Camptocamp SA -# @source JBA and AWST inpiration -# @contributor Grzegorz Grzelak (grzegorz.grzelak@birdglobe.com), -# Joel Grand-Guillaume -# Copyright (c) 2010 Alexis de Lattre (alexis@via.ecp.fr) -# - ported XML-based webservices (Admin.ch, ECB, PL NBP) to new XML lib -# - rates given by ECB webservice is now correct even when main_cur <> EUR -# - rates given by PL_NBP webs. is now correct even when main_cur <> PLN -# - if company_currency <> CHF, you can now update CHF via Admin.ch -# (same for EUR with ECB webservice and PLN with NBP webservice) -# For more details, see Launchpad bug #645263 -# - mecanism to check if rates given by the webservice are "fresh" -# enough to be written in OpenERP -# ('max_delta_days' parameter for each currency update service) -# Ported to OpenERP 7.0 by Lorenzo Battistini -# +# Copyright (c) 2009 CamptoCamp. All rights reserved. +# @author Nicolas Bessi # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -33,767 +19,285 @@ # ############################################################################## -# TODO "nice to have" : restrain the list of currencies that can be added for -# a webservice to the list of currencies supported by the Webservice -# TODO : implement max_delta_days for Yahoo webservice - import logging -import time -from datetime import datetime, timedelta -from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT -from openerp.tools import DEFAULT_SERVER_DATE_FORMAT -from openerp.osv import fields, osv, orm -from openerp.tools.translate import _ + +from datetime import datetime +from dateutil.relativedelta import relativedelta + +from openerp import models, fields, api, _ +from openerp.exceptions import except_orm, Warning + +from ..services.currency_getter import Currency_getter_factory _logger = logging.getLogger(__name__) +_intervalTypes = { + 'days': lambda interval: relativedelta(days=interval), + 'weeks': lambda interval: relativedelta(days=7*interval), + 'months': lambda interval: relativedelta(months=interval), +} -class Currency_rate_update_service(osv.Model): - """Class that tells for wich services wich currencies have to be updated +supported_currency_array = [ + "AED", "AFN", "ALL", "AMD", "ANG", "AOA", "ARS", "AUD", "AWG", "AZN", + "BAM", "BBD", "BDT", "BGN", "BHD", "BIF", "BMD", "BND", "BOB", "BRL", + "BSD", "BTN", "BWP", "BYR", "BZD", "CAD", "CDF", "CHF", "CLP", "CNY", + "COP", "CRC", "CUP", "CVE", "CYP", "CZK", "DJF", "DKK", "DOP", "DZD", + "EEK", "EGP", "ERN", "ETB", "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", "LKR", "LRD", "LSL", "LTL", "LVL", "LYD", "MAD", + "MDL", "MGA", "MKD", "MMK", "MNT", "MOP", "MRO", "MTL", "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", + "SLL", "SOS", "SPL", "SRD", "STD", "SVC", "SYP", "SZL", "THB", "TJS", + "TMM", "TND", "TOP", "TRY", "TTD", "TVD", "TWD", "TZS", "UAH", "UGX", + "USD", "UYU", "UZS", "VEB", "VEF", "VND", "VUV", "WST", "XAF", "XAG", + "XAU", "XCD", "XDR", "XOF", "XPD", "XPF", "XPT", "YER", "ZAR", "ZMK", + "ZWD" +] - """ +RO_BNR_supported_currency_array = ["AED", "AUD", "BGN", "BRL", "CAD", "CHF", + "CNY", "CZK", "DKK", "EGP", "EUR", "GBP", "HUF", "INR", "JPY", "KRW", + "MDL", "MXN", "NOK", "NZD", "PLN", "RON", "RSD", "RUB", "SEK", "TRY", + "UAH", "USD", "XAU", "XDR", "ZAR"] + +CA_BOC_supported_currency_array = ["AED", "ANG", "ARS", "AUD", "BOC", "BRL", + "BSD", "CHF", "CLP", "CNY", "COP", "CZK", "DKK", "EUR", "FJD", "GBP", + "GHS", "GTQ", "HKD", "HNL", "HRK", "HUF", "IDR", "ILS", "INR", "ISK", + "JMD", "JPY", "KRW", "LKR", "MAD", "MMK", "MXN", "MYR", "NOK", "NZD", + "PAB", "PEN", "PHP", "PKR", "PLN", "RON", "RSD", "RUB", "SEK", "SGD", + "THB", "TND", "TRY", "TTD", "TWD", "USD", "VEF", "VND", "XAF", "XCD", + "XPF", "ZAR"] + +CH_ADMIN_supported_currency_array = ['AED', 'ALL', 'ARS', 'AUD', 'AZN', 'BAM', + 'BDT', 'BGN', 'BHD', 'BRL', 'CAD', 'CHF', 'CLP', 'CNY', 'COP', 'CRC', + 'CZK', 'DKK', 'DOP', 'EGP', 'ETB', 'EUR', 'GBP', 'GTQ', 'HKD', 'HNL', + 'HRK', 'HUF', 'IDR', 'ILS', 'INR', 'ISK', 'JPY', 'KES', 'KHR', 'KRW', + 'KWD', 'KYD', 'KZT', 'LBP', 'LKR', 'LTL', 'LVL', 'LYD', 'MAD', 'MUR', + 'MXN', 'MYR', 'NGN', 'NOK', 'NZD', 'OMR', 'PAB', 'PEN', 'PHP', 'PKR', + 'PLN', 'QAR', 'RON', 'RSD', 'RUB', 'SAR', 'SEK', 'SGD', 'THB', 'TND', + 'TRY', 'TWD', 'TZS', 'UAH', 'USD', 'UYU', 'VEF', 'VND', 'ZAR'] + +ECB_supported_currency_array = ['AUD', 'BGN', 'BRL', 'CAD', 'CHF', 'CNY', + 'CZK', 'DKK', 'GBP', 'HKD', 'HRK', 'HUF', 'IDR', 'ILS', 'INR', 'JPY', + 'KRW', 'LTL', 'MXN', 'MYR', 'NOK', 'NZD', 'PHP', 'PLN', 'RON', 'RUB', + 'SEK', 'SGD', 'THB', 'TRY', 'USD', 'ZAR'] + +MX_BdM_supported_currency_array = ["ARS", "AUD", "BBD", "BMD", "BOB", "BRL", + "BSD", "BZD", "CAD", "CHF", "CLP", "CNH", "CNY", "COP", "CRC", "CUP", + "CZK", "DKK", "DOP", "DZD", "EGP", "ESD", "EUR", "FJD", "GBP", "GTQ", + "GYD", "HKD", "HNL", "HUF", "IDR", "ILS", "INR", "IQD", "JMD", "JPY", + "KES", "KRW", "KWD", "MAD", "MYR", "NGN", "NIC", "NOK", "NZD", "PAB", + "PEN", "PHP", "PLN", "PYG", "RON", "RUB", "SAR", "SEK", "SGD", "SVC", + "THB", "TRY", "TTD", "TWD", "UAH", "USD", "USD", "UYP", "VEF", "VND", + "ZAR"] + +PL_NBP_supported_currency_array = ['AUD', 'BGN', 'BRL', 'CAD', 'CHF', 'CLP', + 'CNY', 'CZK', 'DKK', 'EUR', 'GBP', 'HKD', 'HRK', 'HUF', 'IDR', 'ILS', + 'INR', 'ISK', 'JPY', 'KRW', 'LTL', 'MXN', 'MYR', 'NOK', 'NZD', 'PHP', + 'PLN', 'RON', 'RUB', 'SEK', 'SGD', 'THB', 'TRY', 'UAH', 'USD', 'XDR', + 'ZAR'] + + +class Currency_rate_update_service(models.Model): + """Class keep services and currencies that + have to be updated""" _name = "currency.rate.update.service" _description = "Currency Rate Update" - _columns = { - # List of webservicies the value sould be a class name - 'service': fields.selection( - [ - ('Admin_ch_getter', 'Admin.ch'), + + @api.one + @api.constrains('max_delta_days') + def _check_max_delta_days(self): + if self.max_delta_days < 0: + raise Warning(_('Max delta days must be >= 0')) + + @api.one + @api.constrains('interval_number') + def _check_interval_number(self): + if self.interval_number < 0: + raise Warning(_('Interval number must be >= 0')) + + @api.onchange('interval_number') + def _onchange_interval_number(self): + if self.interval_number == 0: + self.note = "%s Service deactivated. Currencies will no longer be updated. \n%s" % ( + fields.Datetime.now(), self.note and self.note or '') + + @api.onchange('service') + def _onchange_service(self): + currency_list = '' + if self.service: + currencies = [] + currency_list = supported_currency_array + company_id = False + if self.company_id.multi_company_currency_enable: + company_id = self.company_id.id + if self.service == 'ECB_getter': + currency_list = ECB_supported_currency_array + if self.service == 'RO_BNR_getter': + currency_list = RO_BNR_supported_currency_array + if self.service == 'CA_BOC_getter': + currency_list = CA_BOC_supported_currency_array + if self.service == 'CH_ADMIN_getter': + currency_list = CH_ADMIN_supported_currency_array + if self.service == 'MX_BdM_getter': + currency_list = MX_BdM_supported_currency_array + if self.service == 'PL_NBP_getter': + currency_list = PL_NBP_supported_currency_array + if company_id: + currencies = self.env['res.currency'].search( + [('name', 'in', currency_list), + '|', ('company_id', '=', company_id), + ('company_id', '=', False)]) + else: + currencies = self.env['res.currency'].search( + [('name', 'in', currency_list), + ('company_id', '=', False)]) + self.currency_list = [(6, 0, [curr.id for curr in currencies])] + + # List of webservicies the value sould be a class name + service = fields.Selection([ + ('CH_ADMIN_getter', 'Admin.ch'), ('ECB_getter', 'European Central Bank'), - ('Yahoo_getter', 'Yahoo Finance '), + ('YAHOO_getter', 'Yahoo Finance'), # Added for polish rates ('PL_NBP_getter', 'Narodowy Bank Polski'), # Added for mexican rates - ('Banxico_getter', 'Banco de México'), + ('MX_BdM_getter', 'Banco de México'), # Bank of Canada is using RSS-CB # http://www.cbwiki.net/wiki/index.php/Specification_1.1 # This RSS format is used by other national banks # (Thailand, Malaysia, Mexico...) ('CA_BOC_getter', 'Bank of Canada - noon rates'), - ], - "Webservice to use", - required=True - ), - # List of currency to update - 'currency_to_update': fields.many2many( - 'res.currency', - 'res_curreny_auto_udate_rel', - 'service_id', - 'currency_id', - 'currency to update with this service', - ), - # Back ref - 'company_id': fields.many2one( - 'res.company', - 'linked company', - ), - # Note fileds that will be used as a logger - 'note': fields.text('update notice'), - 'max_delta_days': fields.integer( - 'Max delta days', - required=True, - help="If the time delta between the " - "rate date given by the webservice and " - "the current date exeeds this value, " - "then the currency rate is not updated in OpenERP." - ), - } - _defaults = {'max_delta_days': lambda *a: 4} - _sql_constraints = [ - ( - 'curr_service_unique', - 'unique (service, company_id)', - _('You can use a service one time per company !') - ) - ] + # Added for romanian rates + ('RO_BNR_getter', 'National Bank of Romania') + ], + "Webservice to use", + required=True) + # List of currencies available on webservice + currency_list = fields.Many2many('res.currency', + 'res_currency_update_avail_rel', + 'service_id', + 'currency_id', + 'Currencies available') + # List of currency to update + currency_to_update = fields.Many2many( + 'res.currency', + 'res_currency_auto_update_rel', + 'service_id', + 'currency_id', + 'Currencies to update with this service') + # Link with company + company_id = fields.Many2one('res.company', 'Linked Company') + # Note fileds that will be used as a logger + note = fields.Text('Update notice') + max_delta_days = fields.Integer('Max delta days', + 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 OpenERP.") + interval_type = fields.Selection([ + ('days', 'Day(s)'), + ('weeks', 'Week(s)'), + ('months', 'Month(s)')], + string='Currency update frequency', + default='days') + interval_number = fields.Integer('Frequency', default=1) + next_run = fields.Date('Next run on', default=fields.Date.today()) - def _check_max_delta_days(self, cr, uid, ids): - for company in self.read(cr, uid, ids, ['max_delta_days']): - if company['max_delta_days'] >= 0: - continue - else: - return False - return True + _sql_constraints = [('curr_service_unique', 'unique (service, company_id)', + _('You can use a service only one time per company !'))] - _constraints = [ - (_check_max_delta_days, - "'Max delta days' must be >= 0", - ['max_delta_days']), - ] - - -class Currency_rate_update(osv.Model): - """Class that handle an ir cron call who will - update currencies based on a web url""" - _name = "currency.rate.update" - _description = "Currency Rate Update" - # Dict that represent a cron object - nextcall_time = datetime.today() + timedelta(days=1) - nextcall = nextcall_time.strftime(DEFAULT_SERVER_DATETIME_FORMAT) - cron = { - 'active': False, - 'priority': 1, - 'interval_number': 1, - 'interval_type': 'weeks', - 'nextcall': nextcall, - 'numbercall': -1, - 'doall': True, - 'model': 'currency.rate.update', - 'function': 'run_currency_update', - 'args': '()', - } - - LOG_NAME = 'cron-rates' - MOD_NAME = 'currency_rate_update: ' - - def get_cron_id(self, cr, uid, context): - """Returns the updater cron's id. - Create one if the cron does not exists - """ - - cron_id = 0 - cron_obj = self.pool.get('ir.cron') - try: - # Finds the cron that send messages - cron_id = cron_obj.search( - cr, - uid, - [ - ('function', 'ilike', self.cron['function']), - ('model', 'ilike', self.cron['model']) - ], - context={ - 'active_test': False - } - ) - cron_id = int(cron_id[0]) - except Exception: - _logger.info('warning cron not found one will be created') - # Ignore if the cron is missing cause we are - # going to create it in db - pass - if not cron_id: - self.cron['name'] = _('Currency Rate Update') - cron_id = cron_obj.create(cr, uid, self.cron, context) - return cron_id - - def save_cron(self, cr, uid, datas, context=None): - """save the cron config data should be a dict""" - cron_id = self.get_cron_id(cr, uid, context) - return self.pool.get('ir.cron').write(cr, uid, [cron_id], datas) - - def run_currency_update(self, cr, uid): - "update currency at the given frequence" + @api.one + def refresh_currency(self): + """Refresh the currencies rates !!for all companies now""" + self.ensure_one() factory = Currency_getter_factory() - curr_obj = self.pool.get('res.currency') - rate_obj = self.pool.get('res.currency.rate') - companies = self.pool.get('res.company').search(cr, uid, []) - for comp in self.pool.get('res.company').browse(cr, uid, companies): - # The multi company currency can beset or no so we handle - # The two case - if not comp.auto_currency_up: - continue - # We fetch the main currency looking for currency with base = true. - # The main rate should be set at 1.00 - main_curr_ids = curr_obj.search( - cr, uid, - [('base', '=', True), ('company_id', '=', comp.id)] - ) - if not main_curr_ids: + curr_obj = self.env['res.currency'] + rate_obj = self.env['res.currency.rate'] + company = self.company_id + # The multi company currency can be set or no so we handle + # The two case + if company.auto_currency_up: + main_currencies = curr_obj.search( + [('base', '=', True), ('company_id', '=', company.id)]) + if not main_currencies: # If we can not find a base currency for this company # we look for one with no company set - main_curr_ids = curr_obj.search( - cr, uid, - [('base', '=', True), ('company_id', '=', False)] - ) - if main_curr_ids: - main_curr_rec = curr_obj.browse(cr, uid, main_curr_ids[0]) + main_currencies = curr_obj.search( + [('base', '=', True), ('company_id', '=', False)]) + if main_currencies: + main_curr = main_currencies[0] else: - raise orm.except_orm( - _('Error!'), - ('There is no base currency set!') - ) - if main_curr_rec.rate != 1: - raise orm.except_orm( - _('Error!'), - ('Base currency rate should be 1.00!') - ) - main_curr = main_curr_rec.name - for service in comp.services_to_use: - note = service.note or '' - try: - # We initalize the class that will handle the request - # and return a dict of rate - getter = factory.register(service.service) - curr_to_fetch = map(lambda x: x.name, - service.currency_to_update) - res, log_info = getter.get_updated_currency( - curr_to_fetch, - main_curr, - service.max_delta_days + raise Warning(_('There is no base currency set!')) + if main_curr.rate != 1: + raise Warning(_('Base currency rate should be 1.00!')) + note = self.note or '' + try: + # We initalize the class that will handle the request + # and return a dict of rate + getter = factory.register(self.service) + curr_to_fetch = map(lambda x: x.name, + self.currency_to_update) + res, log_info = getter.get_updated_currency( + curr_to_fetch, + main_curr.name, + self.max_delta_days ) - rate_name = time.strftime(DEFAULT_SERVER_DATE_FORMAT) - for curr in service.currency_to_update: - if curr.name == main_curr: - continue - do_create = True - for rate in curr.rate_ids: - if rate.name == rate_name: - rate.write({'rate': res[curr.name]}) - do_create = False - break - if do_create: - vals = { - 'currency_id': curr.id, - 'rate': res[curr.name], - 'name': rate_name - } - rate_obj.create( - cr, - uid, - vals, - ) - - # Show the most recent note at the top - msg = "%s \n%s currency updated. %s" % ( - log_info or '', - datetime.today().strftime( - DEFAULT_SERVER_DATETIME_FORMAT - ), - note - ) - service.write({'note': msg}) - except Exception as exc: - error_msg = "\n%s ERROR : %s %s" % ( - datetime.today().strftime( - DEFAULT_SERVER_DATETIME_FORMAT - ), - repr(exc), - note - ) - _logger.info(repr(exc)) - service.write({'note': error_msg}) - - -class AbstractClassError(Exception): - def __str__(self): - return 'Abstract Class' - - def __repr__(self): - return 'Abstract Class' - - -class AbstractMethodError(Exception): - def __str__(self): - return 'Abstract Method' - - def __repr__(self): - return 'Abstract Method' - - -class UnknowClassError(Exception): - def __str__(self): - return 'Unknown Class' - - def __repr__(self): - return 'Unknown Class' - - -class UnsuportedCurrencyError(Exception): - def __init__(self, value): - self.curr = value - - def __str__(self): - return 'Unsupported currency %s' % self.curr - - def __repr__(self): - return 'Unsupported currency %s' % self.curr - - -class Currency_getter_factory(): - """Factory pattern class that will return - a currency getter class base on the name passed - to the register method - - """ - def register(self, class_name): - allowed = [ - 'Admin_ch_getter', - 'PL_NBP_getter', - 'ECB_getter', - 'NYFB_getter', - 'Google_getter', - 'Yahoo_getter', - 'Banxico_getter', - 'CA_BOC_getter', - ] - if class_name in allowed: - class_def = eval(class_name) - return class_def() - else: - raise UnknowClassError - - -class Curreny_getter_interface(object): - "Abstract class of currency getter" - - log_info = " " - - supported_currency_array = [ - 'AED', 'AFN', 'ALL', 'AMD', 'ANG', 'AOA', 'ARS', 'AUD', 'AWG', 'AZN', - 'BAM', 'BBD', 'BDT', 'BGN', 'BHD', 'BIF', 'BMD', 'BND', 'BOB', 'BRL', - 'BSD', 'BTN', 'BWP', 'BYR', 'BZD', 'CAD', 'CDF', 'CHF', 'CLP', 'CNY', - 'COP', 'CRC', 'CUP', 'CVE', 'CYP', 'CZK', 'DJF', 'DKK', 'DOP', 'DZD', - 'EEK', 'EGP', 'ERN', 'ETB', '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', 'LKR', 'LRD', 'LSL', 'LTL', 'LVL', 'LYD', 'MAD', - 'MDL', 'MGA', 'MKD', 'MMK', 'MNT', 'MOP', 'MRO', 'MTL', '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', - 'SLL', 'SOS', 'SPL', 'SRD', 'STD', 'SVC', 'SYP', 'SZL', 'THB', 'TJS', - 'TMM', 'TND', 'TOP', 'TRY', 'TTD', 'TVD', 'TWD', 'TZS', 'UAH', 'UGX', - 'USD', 'UYU', 'UZS', 'VEB', 'VEF', 'VND', 'VUV', 'WST', 'XAF', 'XAG', - 'XAU', 'XCD', 'XDR', 'XOF', 'XPD', 'XPF', 'XPT', 'YER', 'ZAR', 'ZMK', - 'ZWD' - ] - - # Updated currency this arry will contain the final result - updated_currency = {} - - def get_updated_currency(self, currency_array, main_currency, - max_delta_days): - """Interface method that will retrieve the currency - This function has to be reinplemented in child - """ - raise AbstractMethodError - - def validate_cur(self, currency): - """Validate if the currency to update is supported""" - if currency not in self.supported_currency_array: - raise UnsuportedCurrencyError(currency) - - def get_url(self, url): - """Return a string of a get url query""" - try: - import urllib - objfile = urllib.urlopen(url) - rawfile = objfile.read() - objfile.close() - return rawfile - except ImportError: - raise osv.except_osv( - 'Error !', - self.MOD_NAME + 'Unable to import urllib !' - ) - except IOError: - raise osv.except_osv( - 'Error !', - self.MOD_NAME + 'Web Service does not exist !' - ) - - def check_rate_date(self, rate_date, max_delta_days): - """Check date constrains. rate_date must be of datetime type""" - days_delta = (datetime.today() - rate_date).days - if days_delta > max_delta_days: - raise Exception( - 'The rate timestamp (%s) is %d days away from today, ' - 'which is over the limit (%d days). ' - 'Rate not updated in OpenERP.' % (rate_date, - days_delta, - max_delta_days) - ) - - # We always have a warning when rate_date != today - rate_date_str = datetime.strftime(rate_date, - DEFAULT_SERVER_DATE_FORMAT) - if rate_date.date() != datetime.today().date(): - msg = "The rate timestamp (%s) is not today's date" - self.log_info = ("WARNING : %s %s") % (msg, rate_date_str) - _logger.warning(msg, rate_date_str) - - -# Yahoo ####################################################################### -class Yahoo_getter(Curreny_getter_interface): - """Implementation of Currency_getter_factory interface - for Yahoo finance service - """ - - def get_updated_currency(self, currency_array, main_currency, - max_delta_days): - """implementation of abstract method of curreny_getter_interface""" - self.validate_cur(main_currency) - url = ('http://download.finance.yahoo.com/d/' - 'quotes.txt?s="%s"=X&f=sl1c1abg') - if main_currency in currency_array: - currency_array.remove(main_currency) - for curr in currency_array: - self.validate_cur(curr) - res = self.get_url(url % (main_currency + curr)) - val = res.split(',')[1] - if val: - self.updated_currency[curr] = val - else: - raise Exception('Could not update the %s' % (curr)) - - return self.updated_currency, self.log_info - - -# Admin CH #################################################################### -class Admin_ch_getter(Curreny_getter_interface): - """Implementation of Currency_getter_factory interface - for Admin.ch service - - """ - - def rate_retrieve(self, dom, ns, curr): - """Parse a dom node to retrieve currencies data""" - res = {} - xpath_rate_currency = ("/def:wechselkurse/def:devise[@code='%s']/" - "def:kurs/text()") % (curr.lower()) - xpath_rate_ref = ("/def:wechselkurse/def:devise[@code='%s']/" - "def:waehrung/text()") % (curr.lower()) - res['rate_currency'] = float( - dom.xpath(xpath_rate_currency, namespaces=ns)[0] - ) - res['rate_ref'] = float( - (dom.xpath(xpath_rate_ref, namespaces=ns)[0]).split(' ')[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.afd.admin.ch/publicdb/newdb/' - 'mwst_kurse/wechselkurse.php') - # We do not want to update the main currency - if main_currency in currency_array: - currency_array.remove(main_currency) - # Move to new XML lib cf Launchpad bug #645263 - from lxml import etree - _logger.debug("Admin.ch currency rate service : connecting...") - rawfile = self.get_url(url) - dom = etree.fromstring(rawfile) - _logger.debug("Admin.ch sent a valid XML file") - adminch_ns = { - 'def': 'http://www.afd.admin.ch/publicdb/newdb/mwst_kurse' - } - rate_date = dom.xpath( - '/def:wechselkurse/def:datum/text()', - namespaces=adminch_ns - ) - rate_date = rate_date[0] - rate_date_datetime = datetime.strptime(rate_date, - DEFAULT_SERVER_DATE_FORMAT) - self.check_rate_date(rate_date_datetime, max_delta_days) - # we dynamically update supported currencies - self.supported_currency_array = dom.xpath( - "/def:wechselkurse/def:devise/@code", - namespaces=adminch_ns - ) - self.supported_currency_array = [x.upper() for x - in self.supported_currency_array] - self.supported_currency_array.append('CHF') - - _logger.debug( - "Supported currencies = " + str(self.supported_currency_array) - ) - self.validate_cur(main_currency) - if main_currency != 'CHF': - main_curr_data = self.rate_retrieve(dom, adminch_ns, main_currency) - # 1 MAIN_CURRENCY = main_rate CHF - rate_curr = main_curr_data['rate_currency'] - rate_ref = main_curr_data['rate_ref'] - main_rate = rate_curr / rate_ref - for curr in currency_array: - self.validate_cur(curr) - if curr == 'CHF': - rate = main_rate - else: - curr_data = self.rate_retrieve(dom, adminch_ns, curr) - # 1 MAIN_CURRENCY = rate CURR - if main_currency == 'CHF': - rate = curr_data['rate_ref'] / curr_data['rate_currency'] - else: - rate = (main_rate * curr_data['rate_ref'] / - 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 - - -# ECB getter ################################################################# -class ECB_getter(Curreny_getter_interface): - """Implementation of Currency_getter_factory interface - for ECB service - """ - - def rate_retrieve(self, dom, ns, curr): - """Parse a dom node to retrieve- - currencies data - - """ - res = {} - xpath_curr_rate = ("/gesmes:Envelope/def:Cube/def:Cube/" - "def:Cube[@currency='%s']/@rate") % (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.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml' - # Important : as explained on the ECB web site, the currencies are - # at the beginning of the afternoon ; so, until 3 p.m. Paris time - # the currency rates are the ones of trading day N-1 - # http://www.ecb.europa.eu/stats/exchange/eurofxref/html/index.en.html - - # We do not want to update the main currency - if main_currency in currency_array: - currency_array.remove(main_currency) - # Move to new XML lib cf Launchpad bug #645263 - from lxml import etree - _logger.debug("ECB currency rate service : connecting...") - rawfile = self.get_url(url) - dom = etree.fromstring(rawfile) - _logger.debug("ECB sent a valid XML file") - ecb_ns = { - 'gesmes': 'http://www.gesmes.org/xml/2002-08-01', - 'def': 'http://www.ecb.int/vocabulary/2002-08-01/eurofxref' - } - rate_date = dom.xpath('/gesmes:Envelope/def:Cube/def:Cube/@time', - namespaces=ecb_ns)[0] - rate_date_datetime = datetime.strptime(rate_date, - DEFAULT_SERVER_DATE_FORMAT) - self.check_rate_date(rate_date_datetime, max_delta_days) - # We dynamically update supported currencies - self.supported_currency_array = dom.xpath( - "/gesmes:Envelope/def:Cube/def:Cube/def:Cube/@currency", - namespaces=ecb_ns - ) - self.supported_currency_array.append('EUR') - _logger.debug("Supported currencies = %s " % - self.supported_currency_array) - self.validate_cur(main_currency) - if main_currency != 'EUR': - main_curr_data = self.rate_retrieve(dom, ecb_ns, main_currency) - for curr in currency_array: - self.validate_cur(curr) - if curr == 'EUR': - rate = 1 / main_curr_data['rate_currency'] - else: - curr_data = self.rate_retrieve(dom, ecb_ns, curr) - if main_currency == 'EUR': - 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 - - -# PL NBP ###################################################################### -class PL_NBP_getter(Curreny_getter_interface): - """Implementation of Currency_getter_factory interface - for PL NBP service - - """ - - def rate_retrieve(self, dom, ns, curr): - """ Parse a dom node to retrieve - currencies data""" - res = {} - xpath_rate_currency = ("/tabela_kursow/pozycja[kod_waluty='%s']/" - "kurs_sredni/text()") % (curr.upper()) - xpath_rate_ref = ("/tabela_kursow/pozycja[kod_waluty='%s']/" - "przelicznik/text()") % (curr.upper()) - res['rate_currency'] = float( - dom.xpath(xpath_rate_currency, namespaces=ns)[0].replace(',', '.') - ) - res['rate_ref'] = float(dom.xpath(xpath_rate_ref, 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""" - # LastA.xml is always the most recent one - url = 'http://www.nbp.pl/kursy/xml/LastA.xml' - # We do not want to update the main currency - if main_currency in currency_array: - currency_array.remove(main_currency) - # Move to new XML lib cf Launchpad bug #645263 - from lxml import etree - _logger.debug("NBP.pl currency rate service : connecting...") - rawfile = self.get_url(url) - dom = etree.fromstring(rawfile) - ns = {} # Cool, there are no namespaces ! - _logger.debug("NBP.pl sent a valid XML file") - rate_date = dom.xpath('/tabela_kursow/data_publikacji/text()', - namespaces=ns)[0] - rate_date_datetime = datetime.strptime(rate_date, - DEFAULT_SERVER_DATE_FORMAT) - self.check_rate_date(rate_date_datetime, max_delta_days) - # We dynamically update supported currencies - self.supported_currency_array = dom.xpath( - '/tabela_kursow/pozycja/kod_waluty/text()', - namespaces=ns - ) - self.supported_currency_array.append('PLN') - _logger.debug("Supported currencies = %s" % - self.supported_currency_array) - self.validate_cur(main_currency) - if main_currency != 'PLN': - main_curr_data = self.rate_retrieve(dom, ns, main_currency) - # 1 MAIN_CURRENCY = main_rate PLN - main_rate = (main_curr_data['rate_currency'] / - main_curr_data['rate_ref']) - for curr in currency_array: - self.validate_cur(curr) - if curr == 'PLN': - rate = main_rate - else: - curr_data = self.rate_retrieve(dom, ns, curr) - # 1 MAIN_CURRENCY = rate CURR - if main_currency == 'PLN': - rate = curr_data['rate_ref'] / curr_data['rate_currency'] - else: - rate = (main_rate * curr_data['rate_ref'] / - curr_data['rate_currency']) - self.updated_currency[curr] = rate - _logger.debug("Rate retrieved : %s = %s %s" % - (main_currency, rate, curr)) - return self.updated_currency, self.log_info - - -# Banco de México ############################################################# -class Banxico_getter(Curreny_getter_interface): - """Implementation of Currency_getter_factory interface - for Banco de México service - - """ - - def rate_retrieve(self): - """ Get currency exchange from Banxico.xml and proccess it - TODO: Get correct data from xml instead of process string - """ - url = ('http://www.banxico.org.mx/rsscb/rss?' - 'BMXC_canal=pagos&BMXC_idioma=es') - - from xml.dom.minidom import parse - from StringIO import StringIO - - logger = logging.getLogger(__name__) - logger.debug("Banxico currency rate service : connecting...") - rawfile = self.get_url(url) - - dom = parse(StringIO(rawfile)) - logger.debug("Banxico sent a valid XML file") - - value = dom.getElementsByTagName('cb:value')[0] - rate = value.firstChild.nodeValue - - return float(rate) - - def get_updated_currency(self, currency_array, main_currency, - max_delta_days=1): - """implementation of abstract method of Curreny_getter_interface""" - logger = logging.getLogger(__name__) - # we do not want to update the main currency - if main_currency in currency_array: - currency_array.remove(main_currency) - - # Suported currencies - suported = ['MXN', 'USD'] - for curr in currency_array: - if curr in suported: - # Get currency data - main_rate = self.rate_retrieve() - if main_currency == 'MXN': - rate = 1 / main_rate - else: - rate = main_rate - else: - # No other currency supported - continue - - self.updated_currency[curr] = rate - logger.debug("Rate retrieved : %s = %s %s" % - (main_currency, rate, curr)) - - -# CA BOC ##### Bank of Canada ############################################# -class CA_BOC_getter(Curreny_getter_interface): - """Implementation of Curreny_getter_factory interface - for Bank of Canada RSS service - - """ - - def get_updated_currency(self, currency_array, main_currency, - max_delta_days): - """implementation of abstract method of Curreny_getter_interface""" - - # as of Jan 2014 BOC is publishing noon rates for about 60 currencies - url = ('http://www.bankofcanada.ca/stats/assets/' - 'rates_rss/noon/en_%s.xml') - # closing rates are available as well (please note there are only 12 - # currencies reported): - # http://www.bankofcanada.ca/stats/assets/rates_rss/closing/en_%s.xml - - # We do not want to update the main currency - if main_currency in currency_array: - currency_array.remove(main_currency) - - import feedparser - import pytz - from dateutil import parser - - for curr in currency_array: - - _logger.debug("BOC currency rate service : connecting...") - dom = feedparser.parse(url % curr) - - self.validate_cur(curr) - - # check if BOC service is running - if dom.bozo and dom.status != 404: - _logger.error("Bank of Canada - service is down - try again\ - later...") - - # check if BOC sent a valid response for this currency - if dom.status != 200: - _logger.error("Exchange data for %s is not reported by Bank\ - of Canada." % curr) - raise osv.except_osv('Error !', 'Exchange data for %s is not\ - reported by Bank of Canada.' % str(curr)) - - _logger.debug("BOC sent a valid RSS file for: " + curr) - - # check for valid exchange data - if (dom.entries[0].cb_basecurrency == main_currency) and \ - (dom.entries[0].cb_targetcurrency == curr): - rate = dom.entries[0].cb_exchangerate.split('\n', 1)[0] - rate_date_datetime = parser.parse(dom.entries[0].updated)\ - .astimezone(pytz.utc).replace(tzinfo=None) - self.check_rate_date(rate_date_datetime, max_delta_days) - self.updated_currency[curr] = rate - _logger.debug("BOC Rate retrieved : %s = %s %s" % - (main_currency, rate, curr)) - else: - _logger.error( - "Exchange data format error for Bank of Canada -" - "%s. Please check provider data format " - "and/or source code." % curr) - raise osv.except_osv('Error !', - 'Exchange data format error for\ - Bank of Canada - %s !' % str(curr)) - - return self.updated_currency, self.log_info + rate_name = fields.Datetime.to_string(datetime.utcnow().replace( + hour=0, minute=0, second=0, microsecond=0)) + for curr in self.currency_to_update: + if curr.id == main_curr.id: + continue + do_create = True + for rate in curr.rate_ids: + if rate.name == rate_name: + rate.rate= res[curr.name] + do_create = False + break + if do_create: + vals = { + 'currency_id': curr.id, + 'rate': res[curr.name], + 'name': rate_name + } + rate_obj.create(vals) + + # Show the most recent note at the top + msg = "%s \n%s currency updated. %s" % ( + log_info or '', + fields.Datetime.to_string(datetime.today()), + note + ) + self.write({'note': msg}) + except Exception as exc: + error_msg = "\n%s ERROR : %s %s" % ( + fields.Datetime.to_string(datetime.today()), + repr(exc), + note + ) + _logger.info(repr(exc)) + self.write({'note': error_msg}) + if self._context.get('cron', False): + next_run = (datetime.combine( + fields.Date.from_string(self.next_run), + datetime.time.min) + + _intervalTypes[str(self.interval_type)] + (self.interval_number)).date() + self.next_run = next_run + + @api.multi + def run_currency_update(self): + "Update currency at the given frequence" + ctx = dict(self._context) + current_date = fields.Date.today() + services = self.search([('next_run', '=', current_date)]) + ctx['cron'] = True + for service in services: + service.with_context(ctx).refresh_currency() + + @api.model + def _run_currency_update(self): + self.run_currency_update() diff --git a/currency_rate_update/services/currency_rate_update.py b/currency_rate_update/services/currency_rate_update.py new file mode 100644 index 000000000..91419dfdc --- /dev/null +++ b/currency_rate_update/services/currency_rate_update.py @@ -0,0 +1,799 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (c) 2009 Camptocamp SA +# @source JBA and AWST inpiration +# @contributor Grzegorz Grzelak (grzegorz.grzelak@birdglobe.com), +# Joel Grand-Guillaume +# Copyright (c) 2010 Alexis de Lattre (alexis@via.ecp.fr) +# - ported XML-based webservices (Admin.ch, ECB, PL NBP) to new XML lib +# - rates given by ECB webservice is now correct even when main_cur <> EUR +# - rates given by PL_NBP webs. is now correct even when main_cur <> PLN +# - if company_currency <> CHF, you can now update CHF via Admin.ch +# (same for EUR with ECB webservice and PLN with NBP webservice) +# For more details, see Launchpad bug #645263 +# - mecanism to check if rates given by the webservice are "fresh" +# enough to be written in OpenERP +# ('max_delta_days' parameter for each currency update service) +# Ported to OpenERP 7.0 by Lorenzo Battistini +# +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +# TODO "nice to have" : restrain the list of currencies that can be added for +# a webservice to the list of currencies supported by the Webservice +# TODO : implement max_delta_days for Yahoo webservice + +import logging +import time +from datetime import datetime, timedelta +from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT +from openerp.tools import DEFAULT_SERVER_DATE_FORMAT +from openerp.osv import fields, osv, orm +from openerp.tools.translate import _ + +_logger = logging.getLogger(__name__) + + +class Currency_rate_update_service(osv.Model): + """Class that tells for wich services wich currencies have to be updated + + """ + _name = "currency.rate.update.service" + _description = "Currency Rate Update" + _columns = { + # List of webservicies the value sould be a class name + 'service': fields.selection( + [ + ('Admin_ch_getter', 'Admin.ch'), + ('ECB_getter', 'European Central Bank'), + ('Yahoo_getter', 'Yahoo Finance '), + # Added for polish rates + ('PL_NBP_getter', 'Narodowy Bank Polski'), + # Added for mexican rates + ('Banxico_getter', 'Banco de México'), + # Bank of Canada is using RSS-CB + # http://www.cbwiki.net/wiki/index.php/Specification_1.1 + # This RSS format is used by other national banks + # (Thailand, Malaysia, Mexico...) + ('CA_BOC_getter', 'Bank of Canada - noon rates'), + ], + "Webservice to use", + required=True + ), + # List of currency to update + 'currency_to_update': fields.many2many( + 'res.currency', + 'res_curreny_auto_udate_rel', + 'service_id', + 'currency_id', + 'currency to update with this service', + ), + # Back ref + 'company_id': fields.many2one( + 'res.company', + 'linked company', + ), + # Note fileds that will be used as a logger + 'note': fields.text('update notice'), + 'max_delta_days': fields.integer( + 'Max delta days', + required=True, + help="If the time delta between the " + "rate date given by the webservice and " + "the current date exeeds this value, " + "then the currency rate is not updated in OpenERP." + ), + } + _defaults = {'max_delta_days': lambda *a: 4} + _sql_constraints = [ + ( + 'curr_service_unique', + 'unique (service, company_id)', + _('You can use a service one time per company !') + ) + ] + + def _check_max_delta_days(self, cr, uid, ids): + for company in self.read(cr, uid, ids, ['max_delta_days']): + if company['max_delta_days'] >= 0: + continue + else: + return False + return True + + _constraints = [ + (_check_max_delta_days, + "'Max delta days' must be >= 0", + ['max_delta_days']), + ] + + +class Currency_rate_update(osv.Model): + """Class that handle an ir cron call who will + update currencies based on a web url""" + _name = "currency.rate.update" + _description = "Currency Rate Update" + # Dict that represent a cron object + nextcall_time = datetime.today() + timedelta(days=1) + nextcall = nextcall_time.strftime(DEFAULT_SERVER_DATETIME_FORMAT) + cron = { + 'active': False, + 'priority': 1, + 'interval_number': 1, + 'interval_type': 'weeks', + 'nextcall': nextcall, + 'numbercall': -1, + 'doall': True, + 'model': 'currency.rate.update', + 'function': 'run_currency_update', + 'args': '()', + } + + LOG_NAME = 'cron-rates' + MOD_NAME = 'currency_rate_update: ' + + def get_cron_id(self, cr, uid, context): + """Returns the updater cron's id. + Create one if the cron does not exists + """ + + cron_id = 0 + cron_obj = self.pool.get('ir.cron') + try: + # Finds the cron that send messages + cron_id = cron_obj.search( + cr, + uid, + [ + ('function', 'ilike', self.cron['function']), + ('model', 'ilike', self.cron['model']) + ], + context={ + 'active_test': False + } + ) + cron_id = int(cron_id[0]) + except Exception: + _logger.info('warning cron not found one will be created') + # Ignore if the cron is missing cause we are + # going to create it in db + pass + if not cron_id: + self.cron['name'] = _('Currency Rate Update') + cron_id = cron_obj.create(cr, uid, self.cron, context) + return cron_id + + def save_cron(self, cr, uid, datas, context=None): + """save the cron config data should be a dict""" + cron_id = self.get_cron_id(cr, uid, context) + return self.pool.get('ir.cron').write(cr, uid, [cron_id], datas) + + def run_currency_update(self, cr, uid): + "update currency at the given frequence" + factory = Currency_getter_factory() + curr_obj = self.pool.get('res.currency') + rate_obj = self.pool.get('res.currency.rate') + companies = self.pool.get('res.company').search(cr, uid, []) + for comp in self.pool.get('res.company').browse(cr, uid, companies): + # The multi company currency can beset or no so we handle + # The two case + if not comp.auto_currency_up: + continue + # We fetch the main currency looking for currency with base = true. + # The main rate should be set at 1.00 + main_curr_ids = curr_obj.search( + cr, uid, + [('base', '=', True), ('company_id', '=', comp.id)] + ) + if not main_curr_ids: + # If we can not find a base currency for this company + # we look for one with no company set + main_curr_ids = curr_obj.search( + cr, uid, + [('base', '=', True), ('company_id', '=', False)] + ) + if main_curr_ids: + main_curr_rec = curr_obj.browse(cr, uid, main_curr_ids[0]) + else: + raise orm.except_orm( + _('Error!'), + ('There is no base currency set!') + ) + if main_curr_rec.rate != 1: + raise orm.except_orm( + _('Error!'), + ('Base currency rate should be 1.00!') + ) + main_curr = main_curr_rec.name + for service in comp.services_to_use: + note = service.note or '' + try: + # We initalize the class that will handle the request + # and return a dict of rate + getter = factory.register(service.service) + curr_to_fetch = map(lambda x: x.name, + service.currency_to_update) + res, log_info = getter.get_updated_currency( + curr_to_fetch, + main_curr, + service.max_delta_days + ) + rate_name = time.strftime(DEFAULT_SERVER_DATE_FORMAT) + for curr in service.currency_to_update: + if curr.name == main_curr: + continue + do_create = True + for rate in curr.rate_ids: + if rate.name == rate_name: + rate.write({'rate': res[curr.name]}) + do_create = False + break + if do_create: + vals = { + 'currency_id': curr.id, + 'rate': res[curr.name], + 'name': rate_name + } + rate_obj.create( + cr, + uid, + vals, + ) + + # Show the most recent note at the top + msg = "%s \n%s currency updated. %s" % ( + log_info or '', + datetime.today().strftime( + DEFAULT_SERVER_DATETIME_FORMAT + ), + note + ) + service.write({'note': msg}) + except Exception as exc: + error_msg = "\n%s ERROR : %s %s" % ( + datetime.today().strftime( + DEFAULT_SERVER_DATETIME_FORMAT + ), + repr(exc), + note + ) + _logger.info(repr(exc)) + service.write({'note': error_msg}) + + +class AbstractClassError(Exception): + def __str__(self): + return 'Abstract Class' + + def __repr__(self): + return 'Abstract Class' + + +class AbstractMethodError(Exception): + def __str__(self): + return 'Abstract Method' + + def __repr__(self): + return 'Abstract Method' + + +class UnknowClassError(Exception): + def __str__(self): + return 'Unknown Class' + + def __repr__(self): + return 'Unknown Class' + + +class UnsuportedCurrencyError(Exception): + def __init__(self, value): + self.curr = value + + def __str__(self): + return 'Unsupported currency %s' % self.curr + + def __repr__(self): + return 'Unsupported currency %s' % self.curr + + +class Currency_getter_factory(): + """Factory pattern class that will return + a currency getter class base on the name passed + to the register method + + """ + def register(self, class_name): + allowed = [ + 'Admin_ch_getter', + 'PL_NBP_getter', + 'ECB_getter', + 'NYFB_getter', + 'Google_getter', + 'Yahoo_getter', + 'Banxico_getter', + 'CA_BOC_getter', + ] + if class_name in allowed: + class_def = eval(class_name) + return class_def() + else: + raise UnknowClassError + + +class Curreny_getter_interface(object): + "Abstract class of currency getter" + + log_info = " " + + supported_currency_array = [ + 'AED', 'AFN', 'ALL', 'AMD', 'ANG', 'AOA', 'ARS', 'AUD', 'AWG', 'AZN', + 'BAM', 'BBD', 'BDT', 'BGN', 'BHD', 'BIF', 'BMD', 'BND', 'BOB', 'BRL', + 'BSD', 'BTN', 'BWP', 'BYR', 'BZD', 'CAD', 'CDF', 'CHF', 'CLP', 'CNY', + 'COP', 'CRC', 'CUP', 'CVE', 'CYP', 'CZK', 'DJF', 'DKK', 'DOP', 'DZD', + 'EEK', 'EGP', 'ERN', 'ETB', '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', 'LKR', 'LRD', 'LSL', 'LTL', 'LVL', 'LYD', 'MAD', + 'MDL', 'MGA', 'MKD', 'MMK', 'MNT', 'MOP', 'MRO', 'MTL', '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', + 'SLL', 'SOS', 'SPL', 'SRD', 'STD', 'SVC', 'SYP', 'SZL', 'THB', 'TJS', + 'TMM', 'TND', 'TOP', 'TRY', 'TTD', 'TVD', 'TWD', 'TZS', 'UAH', 'UGX', + 'USD', 'UYU', 'UZS', 'VEB', 'VEF', 'VND', 'VUV', 'WST', 'XAF', 'XAG', + 'XAU', 'XCD', 'XDR', 'XOF', 'XPD', 'XPF', 'XPT', 'YER', 'ZAR', 'ZMK', + 'ZWD' + ] + + # Updated currency this arry will contain the final result + updated_currency = {} + + def get_updated_currency(self, currency_array, main_currency, + max_delta_days): + """Interface method that will retrieve the currency + This function has to be reinplemented in child + """ + raise AbstractMethodError + + def validate_cur(self, currency): + """Validate if the currency to update is supported""" + if currency not in self.supported_currency_array: + raise UnsuportedCurrencyError(currency) + + def get_url(self, url): + """Return a string of a get url query""" + try: + import urllib + objfile = urllib.urlopen(url) + rawfile = objfile.read() + objfile.close() + return rawfile + except ImportError: + raise osv.except_osv( + 'Error !', + self.MOD_NAME + 'Unable to import urllib !' + ) + except IOError: + raise osv.except_osv( + 'Error !', + self.MOD_NAME + 'Web Service does not exist !' + ) + + def check_rate_date(self, rate_date, max_delta_days): + """Check date constrains. rate_date must be of datetime type""" + days_delta = (datetime.today() - rate_date).days + if days_delta > max_delta_days: + raise Exception( + 'The rate timestamp (%s) is %d days away from today, ' + 'which is over the limit (%d days). ' + 'Rate not updated in OpenERP.' % (rate_date, + days_delta, + max_delta_days) + ) + + # We always have a warning when rate_date != today + rate_date_str = datetime.strftime(rate_date, + DEFAULT_SERVER_DATE_FORMAT) + if rate_date.date() != datetime.today().date(): + msg = "The rate timestamp (%s) is not today's date" + self.log_info = ("WARNING : %s %s") % (msg, rate_date_str) + _logger.warning(msg, rate_date_str) + + +# Yahoo ####################################################################### +class Yahoo_getter(Curreny_getter_interface): + """Implementation of Currency_getter_factory interface + for Yahoo finance service + """ + + def get_updated_currency(self, currency_array, main_currency, + max_delta_days): + """implementation of abstract method of curreny_getter_interface""" + self.validate_cur(main_currency) + url = ('http://download.finance.yahoo.com/d/' + 'quotes.txt?s="%s"=X&f=sl1c1abg') + if main_currency in currency_array: + currency_array.remove(main_currency) + for curr in currency_array: + self.validate_cur(curr) + res = self.get_url(url % (main_currency + curr)) + val = res.split(',')[1] + if val: + self.updated_currency[curr] = val + else: + raise Exception('Could not update the %s' % (curr)) + + return self.updated_currency, self.log_info + + +# Admin CH #################################################################### +class Admin_ch_getter(Curreny_getter_interface): + """Implementation of Currency_getter_factory interface + for Admin.ch service + + """ + + def rate_retrieve(self, dom, ns, curr): + """Parse a dom node to retrieve currencies data""" + res = {} + xpath_rate_currency = ("/def:wechselkurse/def:devise[@code='%s']/" + "def:kurs/text()") % (curr.lower()) + xpath_rate_ref = ("/def:wechselkurse/def:devise[@code='%s']/" + "def:waehrung/text()") % (curr.lower()) + res['rate_currency'] = float( + dom.xpath(xpath_rate_currency, namespaces=ns)[0] + ) + res['rate_ref'] = float( + (dom.xpath(xpath_rate_ref, namespaces=ns)[0]).split(' ')[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.afd.admin.ch/publicdb/newdb/' + 'mwst_kurse/wechselkurse.php') + # We do not want to update the main currency + if main_currency in currency_array: + currency_array.remove(main_currency) + # Move to new XML lib cf Launchpad bug #645263 + from lxml import etree + _logger.debug("Admin.ch currency rate service : connecting...") + rawfile = self.get_url(url) + dom = etree.fromstring(rawfile) + _logger.debug("Admin.ch sent a valid XML file") + adminch_ns = { + 'def': 'http://www.afd.admin.ch/publicdb/newdb/mwst_kurse' + } + rate_date = dom.xpath( + '/def:wechselkurse/def:datum/text()', + namespaces=adminch_ns + ) + rate_date = rate_date[0] + rate_date_datetime = datetime.strptime(rate_date, + DEFAULT_SERVER_DATE_FORMAT) + self.check_rate_date(rate_date_datetime, max_delta_days) + # we dynamically update supported currencies + self.supported_currency_array = dom.xpath( + "/def:wechselkurse/def:devise/@code", + namespaces=adminch_ns + ) + self.supported_currency_array = [x.upper() for x + in self.supported_currency_array] + self.supported_currency_array.append('CHF') + + _logger.debug( + "Supported currencies = " + str(self.supported_currency_array) + ) + self.validate_cur(main_currency) + if main_currency != 'CHF': + main_curr_data = self.rate_retrieve(dom, adminch_ns, main_currency) + # 1 MAIN_CURRENCY = main_rate CHF + rate_curr = main_curr_data['rate_currency'] + rate_ref = main_curr_data['rate_ref'] + main_rate = rate_curr / rate_ref + for curr in currency_array: + self.validate_cur(curr) + if curr == 'CHF': + rate = main_rate + else: + curr_data = self.rate_retrieve(dom, adminch_ns, curr) + # 1 MAIN_CURRENCY = rate CURR + if main_currency == 'CHF': + rate = curr_data['rate_ref'] / curr_data['rate_currency'] + else: + rate = (main_rate * curr_data['rate_ref'] / + 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 + + +# ECB getter ################################################################# +class ECB_getter(Curreny_getter_interface): + """Implementation of Currency_getter_factory interface + for ECB service + """ + + def rate_retrieve(self, dom, ns, curr): + """Parse a dom node to retrieve- + currencies data + + """ + res = {} + xpath_curr_rate = ("/gesmes:Envelope/def:Cube/def:Cube/" + "def:Cube[@currency='%s']/@rate") % (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.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml' + # Important : as explained on the ECB web site, the currencies are + # at the beginning of the afternoon ; so, until 3 p.m. Paris time + # the currency rates are the ones of trading day N-1 + # http://www.ecb.europa.eu/stats/exchange/eurofxref/html/index.en.html + + # We do not want to update the main currency + if main_currency in currency_array: + currency_array.remove(main_currency) + # Move to new XML lib cf Launchpad bug #645263 + from lxml import etree + _logger.debug("ECB currency rate service : connecting...") + rawfile = self.get_url(url) + dom = etree.fromstring(rawfile) + _logger.debug("ECB sent a valid XML file") + ecb_ns = { + 'gesmes': 'http://www.gesmes.org/xml/2002-08-01', + 'def': 'http://www.ecb.int/vocabulary/2002-08-01/eurofxref' + } + rate_date = dom.xpath('/gesmes:Envelope/def:Cube/def:Cube/@time', + namespaces=ecb_ns)[0] + rate_date_datetime = datetime.strptime(rate_date, + DEFAULT_SERVER_DATE_FORMAT) + self.check_rate_date(rate_date_datetime, max_delta_days) + # We dynamically update supported currencies + self.supported_currency_array = dom.xpath( + "/gesmes:Envelope/def:Cube/def:Cube/def:Cube/@currency", + namespaces=ecb_ns + ) + self.supported_currency_array.append('EUR') + _logger.debug("Supported currencies = %s " % + self.supported_currency_array) + self.validate_cur(main_currency) + if main_currency != 'EUR': + main_curr_data = self.rate_retrieve(dom, ecb_ns, main_currency) + for curr in currency_array: + self.validate_cur(curr) + if curr == 'EUR': + rate = 1 / main_curr_data['rate_currency'] + else: + curr_data = self.rate_retrieve(dom, ecb_ns, curr) + if main_currency == 'EUR': + 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 + + +# PL NBP ###################################################################### +class PL_NBP_getter(Curreny_getter_interface): + """Implementation of Currency_getter_factory interface + for PL NBP service + + """ + + def rate_retrieve(self, dom, ns, curr): + """ Parse a dom node to retrieve + currencies data""" + res = {} + xpath_rate_currency = ("/tabela_kursow/pozycja[kod_waluty='%s']/" + "kurs_sredni/text()") % (curr.upper()) + xpath_rate_ref = ("/tabela_kursow/pozycja[kod_waluty='%s']/" + "przelicznik/text()") % (curr.upper()) + res['rate_currency'] = float( + dom.xpath(xpath_rate_currency, namespaces=ns)[0].replace(',', '.') + ) + res['rate_ref'] = float(dom.xpath(xpath_rate_ref, 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""" + # LastA.xml is always the most recent one + url = 'http://www.nbp.pl/kursy/xml/LastA.xml' + # We do not want to update the main currency + if main_currency in currency_array: + currency_array.remove(main_currency) + # Move to new XML lib cf Launchpad bug #645263 + from lxml import etree + _logger.debug("NBP.pl currency rate service : connecting...") + rawfile = self.get_url(url) + dom = etree.fromstring(rawfile) + ns = {} # Cool, there are no namespaces ! + _logger.debug("NBP.pl sent a valid XML file") + rate_date = dom.xpath('/tabela_kursow/data_publikacji/text()', + namespaces=ns)[0] + rate_date_datetime = datetime.strptime(rate_date, + DEFAULT_SERVER_DATE_FORMAT) + self.check_rate_date(rate_date_datetime, max_delta_days) + # We dynamically update supported currencies + self.supported_currency_array = dom.xpath( + '/tabela_kursow/pozycja/kod_waluty/text()', + namespaces=ns + ) + self.supported_currency_array.append('PLN') + _logger.debug("Supported currencies = %s" % + self.supported_currency_array) + self.validate_cur(main_currency) + if main_currency != 'PLN': + main_curr_data = self.rate_retrieve(dom, ns, main_currency) + # 1 MAIN_CURRENCY = main_rate PLN + main_rate = (main_curr_data['rate_currency'] / + main_curr_data['rate_ref']) + for curr in currency_array: + self.validate_cur(curr) + if curr == 'PLN': + rate = main_rate + else: + curr_data = self.rate_retrieve(dom, ns, curr) + # 1 MAIN_CURRENCY = rate CURR + if main_currency == 'PLN': + rate = curr_data['rate_ref'] / curr_data['rate_currency'] + else: + rate = (main_rate * curr_data['rate_ref'] / + curr_data['rate_currency']) + self.updated_currency[curr] = rate + _logger.debug("Rate retrieved : %s = %s %s" % + (main_currency, rate, curr)) + return self.updated_currency, self.log_info + + +# Banco de México ############################################################# +class Banxico_getter(Curreny_getter_interface): + """Implementation of Currency_getter_factory interface + for Banco de México service + + """ + + def rate_retrieve(self): + """ Get currency exchange from Banxico.xml and proccess it + TODO: Get correct data from xml instead of process string + """ + url = ('http://www.banxico.org.mx/rsscb/rss?' + 'BMXC_canal=pagos&BMXC_idioma=es') + + from xml.dom.minidom import parse + from StringIO import StringIO + + logger = logging.getLogger(__name__) + logger.debug("Banxico currency rate service : connecting...") + rawfile = self.get_url(url) + + dom = parse(StringIO(rawfile)) + logger.debug("Banxico sent a valid XML file") + + value = dom.getElementsByTagName('cb:value')[0] + rate = value.firstChild.nodeValue + + return float(rate) + + def get_updated_currency(self, currency_array, main_currency, + max_delta_days=1): + """implementation of abstract method of Curreny_getter_interface""" + logger = logging.getLogger(__name__) + # we do not want to update the main currency + if main_currency in currency_array: + currency_array.remove(main_currency) + + # Suported currencies + suported = ['MXN', 'USD'] + for curr in currency_array: + if curr in suported: + # Get currency data + main_rate = self.rate_retrieve() + if main_currency == 'MXN': + rate = 1 / main_rate + else: + rate = main_rate + else: + # No other currency supported + continue + + self.updated_currency[curr] = rate + logger.debug("Rate retrieved : %s = %s %s" % + (main_currency, rate, curr)) + + +# CA BOC ##### Bank of Canada ############################################# +class CA_BOC_getter(Curreny_getter_interface): + """Implementation of Curreny_getter_factory interface + for Bank of Canada RSS service + + """ + + def get_updated_currency(self, currency_array, main_currency, + max_delta_days): + """implementation of abstract method of Curreny_getter_interface""" + + # as of Jan 2014 BOC is publishing noon rates for about 60 currencies + url = ('http://www.bankofcanada.ca/stats/assets/' + 'rates_rss/noon/en_%s.xml') + # closing rates are available as well (please note there are only 12 + # currencies reported): + # http://www.bankofcanada.ca/stats/assets/rates_rss/closing/en_%s.xml + + # We do not want to update the main currency + if main_currency in currency_array: + currency_array.remove(main_currency) + + import feedparser + import pytz + from dateutil import parser + + for curr in currency_array: + + _logger.debug("BOC currency rate service : connecting...") + dom = feedparser.parse(url % curr) + + self.validate_cur(curr) + + # check if BOC service is running + if dom.bozo and dom.status != 404: + _logger.error("Bank of Canada - service is down - try again\ + later...") + + # check if BOC sent a valid response for this currency + if dom.status != 200: + _logger.error("Exchange data for %s is not reported by Bank\ + of Canada." % curr) + raise osv.except_osv('Error !', 'Exchange data for %s is not\ + reported by Bank of Canada.' % str(curr)) + + _logger.debug("BOC sent a valid RSS file for: " + curr) + + # check for valid exchange data + if (dom.entries[0].cb_basecurrency == main_currency) and \ + (dom.entries[0].cb_targetcurrency == curr): + rate = dom.entries[0].cb_exchangerate.split('\n', 1)[0] + rate_date_datetime = parser.parse(dom.entries[0].updated)\ + .astimezone(pytz.utc).replace(tzinfo=None) + self.check_rate_date(rate_date_datetime, max_delta_days) + self.updated_currency[curr] = rate + _logger.debug("BOC Rate retrieved : %s = %s %s" % + (main_currency, rate, curr)) + else: + _logger.error( + "Exchange data format error for Bank of Canada -" + "%s. Please check provider data format " + "and/or source code." % curr) + raise osv.except_osv('Error !', + 'Exchange data format error for\ + Bank of Canada - %s !' % str(curr)) + + return self.updated_currency, self.log_info From 95434521d7b5b96c099979642b0d7f719866804d Mon Sep 17 00:00:00 2001 From: Fekete Mihai Date: Mon, 10 Nov 2014 12:38:53 +0200 Subject: [PATCH 06/32] Move init file to model. --- currency_rate_update/{ => model}/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename currency_rate_update/{ => model}/__init__.py (100%) diff --git a/currency_rate_update/__init__.py b/currency_rate_update/model/__init__.py similarity index 100% rename from currency_rate_update/__init__.py rename to currency_rate_update/model/__init__.py From b3f786b36af71743739b40390ff97ca2e8f7daed Mon Sep 17 00:00:00 2001 From: Fekete Mihai Date: Mon, 10 Nov 2014 12:40:19 +0200 Subject: [PATCH 07/32] Update copyrights, colaborators will be visible to readme file. --- currency_rate_update/__init__.py | 22 ++++++++++++++++++++++ currency_rate_update/model/__init__.py | 7 ++----- 2 files changed, 24 insertions(+), 5 deletions(-) create mode 100644 currency_rate_update/__init__.py diff --git a/currency_rate_update/__init__.py b/currency_rate_update/__init__.py new file mode 100644 index 000000000..75d7d0aa3 --- /dev/null +++ b/currency_rate_update/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (c) 2009 CamptoCamp. All rights reserved. +# @author Nicolas Bessi +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from . import model diff --git a/currency_rate_update/model/__init__.py b/currency_rate_update/model/__init__.py index 952ce0fea..e8bdb9041 100644 --- a/currency_rate_update/model/__init__.py +++ b/currency_rate_update/model/__init__.py @@ -1,11 +1,8 @@ # -*- coding: utf-8 -*- ############################################################################## # -# Copyright (c) 2008 Camtocamp SA -# @author JB Aubort, Nicolas Bessi, Joel Grand-Guillaume -# European Central Bank and Polish National Bank -# invented by Grzegorz Grzelak -# Banxico implemented by Agustin Cruz openpyme.mx +# Copyright (c) 2009 CamptoCamp. All rights reserved. +# @author Nicolas Bessi # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as From 6291a7871a9348bde029a3aa08c161f7fdfe0d9e Mon Sep 17 00:00:00 2001 From: Fekete Mihai Date: Mon, 10 Nov 2014 12:42:32 +0200 Subject: [PATCH 08/32] Add readme.md, cron data for services, modify declaration of views. --- currency_rate_update/README.md | 60 +++++++++++++++++ currency_rate_update/__openerp__.py | 67 ++----------------- .../view/service_cron_data.xml | 16 +++++ 3 files changed, 81 insertions(+), 62 deletions(-) create mode 100644 currency_rate_update/README.md create mode 100644 currency_rate_update/view/service_cron_data.xml diff --git a/currency_rate_update/README.md b/currency_rate_update/README.md new file mode 100644 index 000000000..e119d916f --- /dev/null +++ b/currency_rate_update/README.md @@ -0,0 +1,60 @@ +Import exchange rates from the Internet. + +The module is able to use 4 different sources: + +1. Admin.ch + Updated daily, source in CHF. + +2. European Central Bank (ported by Grzegorz Grzelak) + The reference rates are based on the regular daily concertation + procedure between central banks within and outside the European + System of Central Banks, which normally takes place at 2.15 p.m. + (14:15) ECB time. Source in EUR. + http://www.ecb.europa.eu/stats/exchange/eurofxref/html/index.en.html + +3. Yahoo Finance + Updated daily + +4. Polish National Bank (Narodowy Bank Polski) + Takes official rates from www.nbp.pl. Adds rate table symbol in log. + You should check when rates should apply to bookkeeping. + If next day you should change the update hour in schedule settings + because in OpenERP they apply from date of update (date - no hours). + +5. Banxico for USD & MXN (created by Agustín Cruz) + Updated daily + +6. Bank of Canada + +7. National Bank of Romania (Banca Nationala a Romaniei) + +In the 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. + +The update can be set under the company form. +You can set for each services which currency you want to update. +The logs of the update are visible under the service note. +You can active or deactivate the update. +The module uses internal ir-cron feature from OpenERP, so the job is +launched once the server starts if the 'first execute date' is before +the current day. +The module supports multi-company currency in two ways: + +* the currencies are shared, you can set currency update only on one + company +* the currency are separated, you can set currency on every company + separately + +A function field lets you know your currency configuration. + +If in multi-company mode, the base currency will be the first company's +currency found in database. + +Thanks to main contributors: +* Grzegorz Grzelak - grzegorz.grzelak@birdglobe.com (ECB, NBP) +* Alexis de Lattre - alexis@via.ecp.fr +* Joel Grand-Guillaume +* Lorenzo Battistini - lorenzo.battistini@agilebg.com (Port to V7) +* Agustin Cruz - openpyme.mx (BdM) +* Dorin Hongu - dhongu@gmail.com (BNR) diff --git a/currency_rate_update/__openerp__.py b/currency_rate_update/__openerp__.py index 1294a3f80..618b5977f 100644 --- a/currency_rate_update/__openerp__.py +++ b/currency_rate_update/__openerp__.py @@ -1,12 +1,8 @@ # -*- coding: utf-8 -*- ############################################################################## # -# Copyright (c) 2008 Camtocamp SA -# @author JB Aubort, Nicolas Bessi, Joel Grand-Guillaume -# European Central Bank and Polish National Bank by Grzegorz Grzelak -# Ported to OpenERP 7.0 by Lorenzo Battistini -# -# Banxico implemented by Agustin Cruz openpyme.mx +# Copyright (c) 2009 CamptoCamp. All rights reserved. +# @author Nicolas Bessi # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -28,67 +24,14 @@ "author": "Camptocamp", "website": "http://camptocamp.com", "category": "Financial Management/Configuration", - "description": """Import exchange rates from the Internet. - -The module is able to use 4 different sources: - -1. Admin.ch - Updated daily, source in CHF. - -2. European Central Bank (ported by Grzegorz Grzelak) - The reference rates are based on the regular - daily concertation procedure between - central banks within and outside the European System of Central Banks, - which normally takes place at 2.15 p.m. (14:15) ECB time. Source in EUR. - http://www.ecb.europa.eu/stats/exchange/eurofxref/html/index.en.html - -3. Yahoo Finance - Updated daily - -4. Polish National Bank (Narodowy Bank Polski) - (contribution by Grzegorz Grzelak) - Takes official rates from www.nbp.pl. Adds rate table symbol in log. - You should check when rates should apply to bookkeeping. - If next day you should change the update hour in schedule settings - because in OpenERP they apply from - date of update (date - no hours). - -5. Banxico for USD & MXN (created by Agustín Cruz) - Updated daily - -In the 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. - -The update can be set under the company form. -You can set for each services which currency you want to update. -The logs of the update are visible under the service note. -You can active or deactivate the update. -The module uses internal ir_cron feature from OpenERP, -so the job is launched once -the server starts if the 'first execute date' is before the current day. -The module supports multi-company currency in two ways: - -* the currencies are shared, you can set currency update only on one - company -* the currency are separated, you can set currency on every company - separately - -A function field lets you know your currency configuration. - -If in multi-company mode, the base currency will -be the first company's currency -found in database. - -Thanks to main contributors: Grzegorz Grzelak, Alexis de Lattre -""", "depends": [ "base", "account", # Added to ensure account security groups are present ], "data": [ - "currency_rate_update.xml", - "company_view.xml", + "view/service_cron_data.xml", + "view/currency_rate_update.xml", + "view/company_view.xml", "security/security.xml", ], "demo": [], diff --git a/currency_rate_update/view/service_cron_data.xml b/currency_rate_update/view/service_cron_data.xml new file mode 100644 index 000000000..cfc640849 --- /dev/null +++ b/currency_rate_update/view/service_cron_data.xml @@ -0,0 +1,16 @@ + + + + + Currency Rate Update + 1 + days + -1 + + + + (None,) + + + + From dcd29fd64e5b370d240efbe39b534ec6180e2850 Mon Sep 17 00:00:00 2001 From: Fekete Mihai Date: Mon, 10 Nov 2014 12:44:40 +0200 Subject: [PATCH 09/32] Modify views. --- currency_rate_update/view/company_view.xml | 6 +++--- .../view/currency_rate_update.xml | 20 +++++++++++++++---- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/currency_rate_update/view/company_view.xml b/currency_rate_update/view/company_view.xml index 1a2c79b5f..2e33ca86a 100644 --- a/currency_rate_update/view/company_view.xml +++ b/currency_rate_update/view/company_view.xml @@ -8,9 +8,8 @@ - - - + + @@ -21,3 +20,4 @@ + diff --git a/currency_rate_update/view/currency_rate_update.xml b/currency_rate_update/view/currency_rate_update.xml index db73b7fbb..f4fa9da69 100644 --- a/currency_rate_update/view/currency_rate_update.xml +++ b/currency_rate_update/view/currency_rate_update.xml @@ -8,7 +8,9 @@ - + + + @@ -18,10 +20,20 @@ currency.rate.update.service
- - + + + + + + + + + + + - + + From b3806d4b55763c8564a4daa9d1b2de3c4fb70ec2 Mon Sep 17 00:00:00 2001 From: Fekete Mihai Date: Mon, 10 Nov 2014 12:56:14 +0200 Subject: [PATCH 10/32] Added abstract classes, split services in different files. --- currency_rate_update/services/__init__.py | 20 + .../services/currency_getter.py | 87 ++ .../services/currency_getter_interface.py | 142 ++++ .../services/currency_rate_update.py | 799 ------------------ .../services/update_service_CA_BOC.py | 97 +++ .../services/update_service_CH_ADMIN.py | 113 +++ .../services/update_service_ECB.py | 103 +++ .../services/update_service_MX_BdM.py | 82 ++ .../services/update_service_PL_NBP.py | 101 +++ .../services/update_service_RO_BNR.py | 99 +++ .../services/update_service_YAHOO.py | 49 ++ 11 files changed, 893 insertions(+), 799 deletions(-) create mode 100644 currency_rate_update/services/__init__.py create mode 100644 currency_rate_update/services/currency_getter.py create mode 100644 currency_rate_update/services/currency_getter_interface.py delete mode 100644 currency_rate_update/services/currency_rate_update.py create mode 100644 currency_rate_update/services/update_service_CA_BOC.py create mode 100644 currency_rate_update/services/update_service_CH_ADMIN.py create mode 100644 currency_rate_update/services/update_service_ECB.py create mode 100644 currency_rate_update/services/update_service_MX_BdM.py create mode 100644 currency_rate_update/services/update_service_PL_NBP.py create mode 100644 currency_rate_update/services/update_service_RO_BNR.py create mode 100644 currency_rate_update/services/update_service_YAHOO.py diff --git a/currency_rate_update/services/__init__.py b/currency_rate_update/services/__init__.py new file mode 100644 index 000000000..5c70dd472 --- /dev/null +++ b/currency_rate_update/services/__init__.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (c) 2009 CamptoCamp. All rights reserved. +# @author Nicolas Bessi +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## diff --git a/currency_rate_update/services/currency_getter.py b/currency_rate_update/services/currency_getter.py new file mode 100644 index 000000000..bc5cf5a51 --- /dev/null +++ b/currency_rate_update/services/currency_getter.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (c) 2009 CamptoCamp. All rights reserved. +# @author Nicolas Bessi +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from update_service_ECB import ECB_getter +from update_service_YAHOO import YAHOO_getter + +from update_service_CA_BOC import CA_BOC_getter +from update_service_CH_ADMIN import CH_ADMIN_getter +from update_service_MX_BdM import MX_BdM_getter +from update_service_PL_NBP import PL_NBP_getter +from update_service_RO_BNR import RO_BNR_getter + +class AbstractClassError(Exception): + def __str__(self): + return 'Abstract Class' + + def __repr__(self): + return 'Abstract Class' + + +class AbstractMethodError(Exception): + def __str__(self): + return 'Abstract Method' + + def __repr__(self): + return 'Abstract Method' + + +class UnknowClassError(Exception): + def __str__(self): + return 'Unknown Class' + + def __repr__(self): + return 'Unknown Class' + + +class UnsuportedCurrencyError(Exception): + def __init__(self, value): + self.curr = value + + def __str__(self): + return 'Unsupported currency %s' % self.curr + + def __repr__(self): + return 'Unsupported currency %s' % self.curr + + +class Currency_getter_factory(): + """Factory pattern class that will return + a currency getter class base on the name passed + to the register method + + """ + def register(self, class_name): + allowed = [ + 'CH_ADMIN_getter', + 'PL_NBP_getter', + 'ECB_getter', + 'GOOGLE_getter', + 'YAHOO_getter', + 'MX_BdM_getter', + 'CA_BOC_getter', + 'RO_BNR_getter', + ] + if class_name in allowed: + class_def = eval(class_name) + return class_def() + else: + raise UnknowClassError diff --git a/currency_rate_update/services/currency_getter_interface.py b/currency_rate_update/services/currency_getter_interface.py new file mode 100644 index 000000000..712507d09 --- /dev/null +++ b/currency_rate_update/services/currency_getter_interface.py @@ -0,0 +1,142 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (c) 2009 CamptoCamp. All rights reserved. +# @author Nicolas Bessi +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +import logging + +from datetime import datetime +from openerp import fields +from openerp.exceptions import except_orm + +_logger = logging.getLogger(__name__) + +class AbstractClassError(Exception): + def __str__(self): + return 'Abstract Class' + + def __repr__(self): + return 'Abstract Class' + + +class AbstractMethodError(Exception): + def __str__(self): + return 'Abstract Method' + + def __repr__(self): + return 'Abstract Method' + + +class UnknowClassError(Exception): + def __str__(self): + return 'Unknown Class' + + def __repr__(self): + return 'Unknown Class' + + +class UnsuportedCurrencyError(Exception): + def __init__(self, value): + self.curr = value + + def __str__(self): + return 'Unsupported currency %s' % self.curr + + def __repr__(self): + return 'Unsupported currency %s' % self.curr + +class Currency_getter_interface(object): + "Abstract class of currency getter" + + log_info = " " + + supported_currency_array = [ + 'AED', 'AFN', 'ALL', 'AMD', 'ANG', 'AOA', 'ARS', 'AUD', 'AWG', 'AZN', + 'BAM', 'BBD', 'BDT', 'BGN', 'BHD', 'BIF', 'BMD', 'BND', 'BOB', 'BRL', + 'BSD', 'BTN', 'BWP', 'BYR', 'BZD', 'CAD', 'CDF', 'CHF', 'CLP', 'CNY', + 'COP', 'CRC', 'CUP', 'CVE', 'CYP', 'CZK', 'DJF', 'DKK', 'DOP', 'DZD', + 'EEK', 'EGP', 'ERN', 'ETB', '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', 'LKR', 'LRD', 'LSL', 'LTL', 'LVL', 'LYD', 'MAD', + 'MDL', 'MGA', 'MKD', 'MMK', 'MNT', 'MOP', 'MRO', 'MTL', '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', + 'SLL', 'SOS', 'SPL', 'SRD', 'STD', 'SVC', 'SYP', 'SZL', 'THB', 'TJS', + 'TMM', 'TND', 'TOP', 'TRY', 'TTD', 'TVD', 'TWD', 'TZS', 'UAH', 'UGX', + 'USD', 'UYU', 'UZS', 'VEB', 'VEF', 'VND', 'VUV', 'WST', 'XAF', 'XAG', + 'XAU', 'XCD', 'XDR', 'XOF', 'XPD', 'XPF', 'XPT', 'YER', 'ZAR', 'ZMK', + 'ZWD' + ] + + # Updated currency this arry will contain the final result + updated_currency = {} + + def get_updated_currency(self, currency_array, main_currency, + max_delta_days): + """Interface method that will retrieve the currency + This function has to be reinplemented in child + """ + raise AbstractMethodError + + def validate_cur(self, currency): + """Validate if the currency to update is supported""" + if currency not in self.supported_currency_array: + raise UnsuportedCurrencyError(currency) + + def get_url(self, url): + """Return a string of a get url query""" + try: + import urllib + objfile = urllib.urlopen(url) + rawfile = objfile.read() + objfile.close() + return rawfile + except ImportError: + raise except_orm( + 'Error !', + self.MOD_NAME + 'Unable to import urllib !' + ) + except IOError: + raise except_orm( + 'Error !', + self.MOD_NAME + 'Web Service does not exist !' + ) + + def check_rate_date(self, rate_date, max_delta_days): + """Check date constrains. rate_date must be of datetime type""" + days_delta = (datetime.today() - rate_date).days + if days_delta > max_delta_days: + raise Exception( + 'The rate timestamp %s is %d days away from today, ' + 'which is over the limit (%d days). ' + 'Rate not updated in OpenERP.' % (rate_date, + days_delta, + max_delta_days) + ) + + # We always have a warning when rate_date != today + if rate_date.date() != datetime.today().date(): + rate_date_str = fields.Date.to_string(rate_date) + msg = "The rate timestamp %s is not today's date %s" % ( + rate_date_str, fields.Date.today()) + self.log_info = ("\n WARNING : %s") % msg + _logger.warning(msg) diff --git a/currency_rate_update/services/currency_rate_update.py b/currency_rate_update/services/currency_rate_update.py deleted file mode 100644 index 91419dfdc..000000000 --- a/currency_rate_update/services/currency_rate_update.py +++ /dev/null @@ -1,799 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Copyright (c) 2009 Camptocamp SA -# @source JBA and AWST inpiration -# @contributor Grzegorz Grzelak (grzegorz.grzelak@birdglobe.com), -# Joel Grand-Guillaume -# Copyright (c) 2010 Alexis de Lattre (alexis@via.ecp.fr) -# - ported XML-based webservices (Admin.ch, ECB, PL NBP) to new XML lib -# - rates given by ECB webservice is now correct even when main_cur <> EUR -# - rates given by PL_NBP webs. is now correct even when main_cur <> PLN -# - if company_currency <> CHF, you can now update CHF via Admin.ch -# (same for EUR with ECB webservice and PLN with NBP webservice) -# For more details, see Launchpad bug #645263 -# - mecanism to check if rates given by the webservice are "fresh" -# enough to be written in OpenERP -# ('max_delta_days' parameter for each currency update service) -# Ported to OpenERP 7.0 by Lorenzo Battistini -# -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -# TODO "nice to have" : restrain the list of currencies that can be added for -# a webservice to the list of currencies supported by the Webservice -# TODO : implement max_delta_days for Yahoo webservice - -import logging -import time -from datetime import datetime, timedelta -from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT -from openerp.tools import DEFAULT_SERVER_DATE_FORMAT -from openerp.osv import fields, osv, orm -from openerp.tools.translate import _ - -_logger = logging.getLogger(__name__) - - -class Currency_rate_update_service(osv.Model): - """Class that tells for wich services wich currencies have to be updated - - """ - _name = "currency.rate.update.service" - _description = "Currency Rate Update" - _columns = { - # List of webservicies the value sould be a class name - 'service': fields.selection( - [ - ('Admin_ch_getter', 'Admin.ch'), - ('ECB_getter', 'European Central Bank'), - ('Yahoo_getter', 'Yahoo Finance '), - # Added for polish rates - ('PL_NBP_getter', 'Narodowy Bank Polski'), - # Added for mexican rates - ('Banxico_getter', 'Banco de México'), - # Bank of Canada is using RSS-CB - # http://www.cbwiki.net/wiki/index.php/Specification_1.1 - # This RSS format is used by other national banks - # (Thailand, Malaysia, Mexico...) - ('CA_BOC_getter', 'Bank of Canada - noon rates'), - ], - "Webservice to use", - required=True - ), - # List of currency to update - 'currency_to_update': fields.many2many( - 'res.currency', - 'res_curreny_auto_udate_rel', - 'service_id', - 'currency_id', - 'currency to update with this service', - ), - # Back ref - 'company_id': fields.many2one( - 'res.company', - 'linked company', - ), - # Note fileds that will be used as a logger - 'note': fields.text('update notice'), - 'max_delta_days': fields.integer( - 'Max delta days', - required=True, - help="If the time delta between the " - "rate date given by the webservice and " - "the current date exeeds this value, " - "then the currency rate is not updated in OpenERP." - ), - } - _defaults = {'max_delta_days': lambda *a: 4} - _sql_constraints = [ - ( - 'curr_service_unique', - 'unique (service, company_id)', - _('You can use a service one time per company !') - ) - ] - - def _check_max_delta_days(self, cr, uid, ids): - for company in self.read(cr, uid, ids, ['max_delta_days']): - if company['max_delta_days'] >= 0: - continue - else: - return False - return True - - _constraints = [ - (_check_max_delta_days, - "'Max delta days' must be >= 0", - ['max_delta_days']), - ] - - -class Currency_rate_update(osv.Model): - """Class that handle an ir cron call who will - update currencies based on a web url""" - _name = "currency.rate.update" - _description = "Currency Rate Update" - # Dict that represent a cron object - nextcall_time = datetime.today() + timedelta(days=1) - nextcall = nextcall_time.strftime(DEFAULT_SERVER_DATETIME_FORMAT) - cron = { - 'active': False, - 'priority': 1, - 'interval_number': 1, - 'interval_type': 'weeks', - 'nextcall': nextcall, - 'numbercall': -1, - 'doall': True, - 'model': 'currency.rate.update', - 'function': 'run_currency_update', - 'args': '()', - } - - LOG_NAME = 'cron-rates' - MOD_NAME = 'currency_rate_update: ' - - def get_cron_id(self, cr, uid, context): - """Returns the updater cron's id. - Create one if the cron does not exists - """ - - cron_id = 0 - cron_obj = self.pool.get('ir.cron') - try: - # Finds the cron that send messages - cron_id = cron_obj.search( - cr, - uid, - [ - ('function', 'ilike', self.cron['function']), - ('model', 'ilike', self.cron['model']) - ], - context={ - 'active_test': False - } - ) - cron_id = int(cron_id[0]) - except Exception: - _logger.info('warning cron not found one will be created') - # Ignore if the cron is missing cause we are - # going to create it in db - pass - if not cron_id: - self.cron['name'] = _('Currency Rate Update') - cron_id = cron_obj.create(cr, uid, self.cron, context) - return cron_id - - def save_cron(self, cr, uid, datas, context=None): - """save the cron config data should be a dict""" - cron_id = self.get_cron_id(cr, uid, context) - return self.pool.get('ir.cron').write(cr, uid, [cron_id], datas) - - def run_currency_update(self, cr, uid): - "update currency at the given frequence" - factory = Currency_getter_factory() - curr_obj = self.pool.get('res.currency') - rate_obj = self.pool.get('res.currency.rate') - companies = self.pool.get('res.company').search(cr, uid, []) - for comp in self.pool.get('res.company').browse(cr, uid, companies): - # The multi company currency can beset or no so we handle - # The two case - if not comp.auto_currency_up: - continue - # We fetch the main currency looking for currency with base = true. - # The main rate should be set at 1.00 - main_curr_ids = curr_obj.search( - cr, uid, - [('base', '=', True), ('company_id', '=', comp.id)] - ) - if not main_curr_ids: - # If we can not find a base currency for this company - # we look for one with no company set - main_curr_ids = curr_obj.search( - cr, uid, - [('base', '=', True), ('company_id', '=', False)] - ) - if main_curr_ids: - main_curr_rec = curr_obj.browse(cr, uid, main_curr_ids[0]) - else: - raise orm.except_orm( - _('Error!'), - ('There is no base currency set!') - ) - if main_curr_rec.rate != 1: - raise orm.except_orm( - _('Error!'), - ('Base currency rate should be 1.00!') - ) - main_curr = main_curr_rec.name - for service in comp.services_to_use: - note = service.note or '' - try: - # We initalize the class that will handle the request - # and return a dict of rate - getter = factory.register(service.service) - curr_to_fetch = map(lambda x: x.name, - service.currency_to_update) - res, log_info = getter.get_updated_currency( - curr_to_fetch, - main_curr, - service.max_delta_days - ) - rate_name = time.strftime(DEFAULT_SERVER_DATE_FORMAT) - for curr in service.currency_to_update: - if curr.name == main_curr: - continue - do_create = True - for rate in curr.rate_ids: - if rate.name == rate_name: - rate.write({'rate': res[curr.name]}) - do_create = False - break - if do_create: - vals = { - 'currency_id': curr.id, - 'rate': res[curr.name], - 'name': rate_name - } - rate_obj.create( - cr, - uid, - vals, - ) - - # Show the most recent note at the top - msg = "%s \n%s currency updated. %s" % ( - log_info or '', - datetime.today().strftime( - DEFAULT_SERVER_DATETIME_FORMAT - ), - note - ) - service.write({'note': msg}) - except Exception as exc: - error_msg = "\n%s ERROR : %s %s" % ( - datetime.today().strftime( - DEFAULT_SERVER_DATETIME_FORMAT - ), - repr(exc), - note - ) - _logger.info(repr(exc)) - service.write({'note': error_msg}) - - -class AbstractClassError(Exception): - def __str__(self): - return 'Abstract Class' - - def __repr__(self): - return 'Abstract Class' - - -class AbstractMethodError(Exception): - def __str__(self): - return 'Abstract Method' - - def __repr__(self): - return 'Abstract Method' - - -class UnknowClassError(Exception): - def __str__(self): - return 'Unknown Class' - - def __repr__(self): - return 'Unknown Class' - - -class UnsuportedCurrencyError(Exception): - def __init__(self, value): - self.curr = value - - def __str__(self): - return 'Unsupported currency %s' % self.curr - - def __repr__(self): - return 'Unsupported currency %s' % self.curr - - -class Currency_getter_factory(): - """Factory pattern class that will return - a currency getter class base on the name passed - to the register method - - """ - def register(self, class_name): - allowed = [ - 'Admin_ch_getter', - 'PL_NBP_getter', - 'ECB_getter', - 'NYFB_getter', - 'Google_getter', - 'Yahoo_getter', - 'Banxico_getter', - 'CA_BOC_getter', - ] - if class_name in allowed: - class_def = eval(class_name) - return class_def() - else: - raise UnknowClassError - - -class Curreny_getter_interface(object): - "Abstract class of currency getter" - - log_info = " " - - supported_currency_array = [ - 'AED', 'AFN', 'ALL', 'AMD', 'ANG', 'AOA', 'ARS', 'AUD', 'AWG', 'AZN', - 'BAM', 'BBD', 'BDT', 'BGN', 'BHD', 'BIF', 'BMD', 'BND', 'BOB', 'BRL', - 'BSD', 'BTN', 'BWP', 'BYR', 'BZD', 'CAD', 'CDF', 'CHF', 'CLP', 'CNY', - 'COP', 'CRC', 'CUP', 'CVE', 'CYP', 'CZK', 'DJF', 'DKK', 'DOP', 'DZD', - 'EEK', 'EGP', 'ERN', 'ETB', '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', 'LKR', 'LRD', 'LSL', 'LTL', 'LVL', 'LYD', 'MAD', - 'MDL', 'MGA', 'MKD', 'MMK', 'MNT', 'MOP', 'MRO', 'MTL', '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', - 'SLL', 'SOS', 'SPL', 'SRD', 'STD', 'SVC', 'SYP', 'SZL', 'THB', 'TJS', - 'TMM', 'TND', 'TOP', 'TRY', 'TTD', 'TVD', 'TWD', 'TZS', 'UAH', 'UGX', - 'USD', 'UYU', 'UZS', 'VEB', 'VEF', 'VND', 'VUV', 'WST', 'XAF', 'XAG', - 'XAU', 'XCD', 'XDR', 'XOF', 'XPD', 'XPF', 'XPT', 'YER', 'ZAR', 'ZMK', - 'ZWD' - ] - - # Updated currency this arry will contain the final result - updated_currency = {} - - def get_updated_currency(self, currency_array, main_currency, - max_delta_days): - """Interface method that will retrieve the currency - This function has to be reinplemented in child - """ - raise AbstractMethodError - - def validate_cur(self, currency): - """Validate if the currency to update is supported""" - if currency not in self.supported_currency_array: - raise UnsuportedCurrencyError(currency) - - def get_url(self, url): - """Return a string of a get url query""" - try: - import urllib - objfile = urllib.urlopen(url) - rawfile = objfile.read() - objfile.close() - return rawfile - except ImportError: - raise osv.except_osv( - 'Error !', - self.MOD_NAME + 'Unable to import urllib !' - ) - except IOError: - raise osv.except_osv( - 'Error !', - self.MOD_NAME + 'Web Service does not exist !' - ) - - def check_rate_date(self, rate_date, max_delta_days): - """Check date constrains. rate_date must be of datetime type""" - days_delta = (datetime.today() - rate_date).days - if days_delta > max_delta_days: - raise Exception( - 'The rate timestamp (%s) is %d days away from today, ' - 'which is over the limit (%d days). ' - 'Rate not updated in OpenERP.' % (rate_date, - days_delta, - max_delta_days) - ) - - # We always have a warning when rate_date != today - rate_date_str = datetime.strftime(rate_date, - DEFAULT_SERVER_DATE_FORMAT) - if rate_date.date() != datetime.today().date(): - msg = "The rate timestamp (%s) is not today's date" - self.log_info = ("WARNING : %s %s") % (msg, rate_date_str) - _logger.warning(msg, rate_date_str) - - -# Yahoo ####################################################################### -class Yahoo_getter(Curreny_getter_interface): - """Implementation of Currency_getter_factory interface - for Yahoo finance service - """ - - def get_updated_currency(self, currency_array, main_currency, - max_delta_days): - """implementation of abstract method of curreny_getter_interface""" - self.validate_cur(main_currency) - url = ('http://download.finance.yahoo.com/d/' - 'quotes.txt?s="%s"=X&f=sl1c1abg') - if main_currency in currency_array: - currency_array.remove(main_currency) - for curr in currency_array: - self.validate_cur(curr) - res = self.get_url(url % (main_currency + curr)) - val = res.split(',')[1] - if val: - self.updated_currency[curr] = val - else: - raise Exception('Could not update the %s' % (curr)) - - return self.updated_currency, self.log_info - - -# Admin CH #################################################################### -class Admin_ch_getter(Curreny_getter_interface): - """Implementation of Currency_getter_factory interface - for Admin.ch service - - """ - - def rate_retrieve(self, dom, ns, curr): - """Parse a dom node to retrieve currencies data""" - res = {} - xpath_rate_currency = ("/def:wechselkurse/def:devise[@code='%s']/" - "def:kurs/text()") % (curr.lower()) - xpath_rate_ref = ("/def:wechselkurse/def:devise[@code='%s']/" - "def:waehrung/text()") % (curr.lower()) - res['rate_currency'] = float( - dom.xpath(xpath_rate_currency, namespaces=ns)[0] - ) - res['rate_ref'] = float( - (dom.xpath(xpath_rate_ref, namespaces=ns)[0]).split(' ')[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.afd.admin.ch/publicdb/newdb/' - 'mwst_kurse/wechselkurse.php') - # We do not want to update the main currency - if main_currency in currency_array: - currency_array.remove(main_currency) - # Move to new XML lib cf Launchpad bug #645263 - from lxml import etree - _logger.debug("Admin.ch currency rate service : connecting...") - rawfile = self.get_url(url) - dom = etree.fromstring(rawfile) - _logger.debug("Admin.ch sent a valid XML file") - adminch_ns = { - 'def': 'http://www.afd.admin.ch/publicdb/newdb/mwst_kurse' - } - rate_date = dom.xpath( - '/def:wechselkurse/def:datum/text()', - namespaces=adminch_ns - ) - rate_date = rate_date[0] - rate_date_datetime = datetime.strptime(rate_date, - DEFAULT_SERVER_DATE_FORMAT) - self.check_rate_date(rate_date_datetime, max_delta_days) - # we dynamically update supported currencies - self.supported_currency_array = dom.xpath( - "/def:wechselkurse/def:devise/@code", - namespaces=adminch_ns - ) - self.supported_currency_array = [x.upper() for x - in self.supported_currency_array] - self.supported_currency_array.append('CHF') - - _logger.debug( - "Supported currencies = " + str(self.supported_currency_array) - ) - self.validate_cur(main_currency) - if main_currency != 'CHF': - main_curr_data = self.rate_retrieve(dom, adminch_ns, main_currency) - # 1 MAIN_CURRENCY = main_rate CHF - rate_curr = main_curr_data['rate_currency'] - rate_ref = main_curr_data['rate_ref'] - main_rate = rate_curr / rate_ref - for curr in currency_array: - self.validate_cur(curr) - if curr == 'CHF': - rate = main_rate - else: - curr_data = self.rate_retrieve(dom, adminch_ns, curr) - # 1 MAIN_CURRENCY = rate CURR - if main_currency == 'CHF': - rate = curr_data['rate_ref'] / curr_data['rate_currency'] - else: - rate = (main_rate * curr_data['rate_ref'] / - 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 - - -# ECB getter ################################################################# -class ECB_getter(Curreny_getter_interface): - """Implementation of Currency_getter_factory interface - for ECB service - """ - - def rate_retrieve(self, dom, ns, curr): - """Parse a dom node to retrieve- - currencies data - - """ - res = {} - xpath_curr_rate = ("/gesmes:Envelope/def:Cube/def:Cube/" - "def:Cube[@currency='%s']/@rate") % (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.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml' - # Important : as explained on the ECB web site, the currencies are - # at the beginning of the afternoon ; so, until 3 p.m. Paris time - # the currency rates are the ones of trading day N-1 - # http://www.ecb.europa.eu/stats/exchange/eurofxref/html/index.en.html - - # We do not want to update the main currency - if main_currency in currency_array: - currency_array.remove(main_currency) - # Move to new XML lib cf Launchpad bug #645263 - from lxml import etree - _logger.debug("ECB currency rate service : connecting...") - rawfile = self.get_url(url) - dom = etree.fromstring(rawfile) - _logger.debug("ECB sent a valid XML file") - ecb_ns = { - 'gesmes': 'http://www.gesmes.org/xml/2002-08-01', - 'def': 'http://www.ecb.int/vocabulary/2002-08-01/eurofxref' - } - rate_date = dom.xpath('/gesmes:Envelope/def:Cube/def:Cube/@time', - namespaces=ecb_ns)[0] - rate_date_datetime = datetime.strptime(rate_date, - DEFAULT_SERVER_DATE_FORMAT) - self.check_rate_date(rate_date_datetime, max_delta_days) - # We dynamically update supported currencies - self.supported_currency_array = dom.xpath( - "/gesmes:Envelope/def:Cube/def:Cube/def:Cube/@currency", - namespaces=ecb_ns - ) - self.supported_currency_array.append('EUR') - _logger.debug("Supported currencies = %s " % - self.supported_currency_array) - self.validate_cur(main_currency) - if main_currency != 'EUR': - main_curr_data = self.rate_retrieve(dom, ecb_ns, main_currency) - for curr in currency_array: - self.validate_cur(curr) - if curr == 'EUR': - rate = 1 / main_curr_data['rate_currency'] - else: - curr_data = self.rate_retrieve(dom, ecb_ns, curr) - if main_currency == 'EUR': - 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 - - -# PL NBP ###################################################################### -class PL_NBP_getter(Curreny_getter_interface): - """Implementation of Currency_getter_factory interface - for PL NBP service - - """ - - def rate_retrieve(self, dom, ns, curr): - """ Parse a dom node to retrieve - currencies data""" - res = {} - xpath_rate_currency = ("/tabela_kursow/pozycja[kod_waluty='%s']/" - "kurs_sredni/text()") % (curr.upper()) - xpath_rate_ref = ("/tabela_kursow/pozycja[kod_waluty='%s']/" - "przelicznik/text()") % (curr.upper()) - res['rate_currency'] = float( - dom.xpath(xpath_rate_currency, namespaces=ns)[0].replace(',', '.') - ) - res['rate_ref'] = float(dom.xpath(xpath_rate_ref, 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""" - # LastA.xml is always the most recent one - url = 'http://www.nbp.pl/kursy/xml/LastA.xml' - # We do not want to update the main currency - if main_currency in currency_array: - currency_array.remove(main_currency) - # Move to new XML lib cf Launchpad bug #645263 - from lxml import etree - _logger.debug("NBP.pl currency rate service : connecting...") - rawfile = self.get_url(url) - dom = etree.fromstring(rawfile) - ns = {} # Cool, there are no namespaces ! - _logger.debug("NBP.pl sent a valid XML file") - rate_date = dom.xpath('/tabela_kursow/data_publikacji/text()', - namespaces=ns)[0] - rate_date_datetime = datetime.strptime(rate_date, - DEFAULT_SERVER_DATE_FORMAT) - self.check_rate_date(rate_date_datetime, max_delta_days) - # We dynamically update supported currencies - self.supported_currency_array = dom.xpath( - '/tabela_kursow/pozycja/kod_waluty/text()', - namespaces=ns - ) - self.supported_currency_array.append('PLN') - _logger.debug("Supported currencies = %s" % - self.supported_currency_array) - self.validate_cur(main_currency) - if main_currency != 'PLN': - main_curr_data = self.rate_retrieve(dom, ns, main_currency) - # 1 MAIN_CURRENCY = main_rate PLN - main_rate = (main_curr_data['rate_currency'] / - main_curr_data['rate_ref']) - for curr in currency_array: - self.validate_cur(curr) - if curr == 'PLN': - rate = main_rate - else: - curr_data = self.rate_retrieve(dom, ns, curr) - # 1 MAIN_CURRENCY = rate CURR - if main_currency == 'PLN': - rate = curr_data['rate_ref'] / curr_data['rate_currency'] - else: - rate = (main_rate * curr_data['rate_ref'] / - curr_data['rate_currency']) - self.updated_currency[curr] = rate - _logger.debug("Rate retrieved : %s = %s %s" % - (main_currency, rate, curr)) - return self.updated_currency, self.log_info - - -# Banco de México ############################################################# -class Banxico_getter(Curreny_getter_interface): - """Implementation of Currency_getter_factory interface - for Banco de México service - - """ - - def rate_retrieve(self): - """ Get currency exchange from Banxico.xml and proccess it - TODO: Get correct data from xml instead of process string - """ - url = ('http://www.banxico.org.mx/rsscb/rss?' - 'BMXC_canal=pagos&BMXC_idioma=es') - - from xml.dom.minidom import parse - from StringIO import StringIO - - logger = logging.getLogger(__name__) - logger.debug("Banxico currency rate service : connecting...") - rawfile = self.get_url(url) - - dom = parse(StringIO(rawfile)) - logger.debug("Banxico sent a valid XML file") - - value = dom.getElementsByTagName('cb:value')[0] - rate = value.firstChild.nodeValue - - return float(rate) - - def get_updated_currency(self, currency_array, main_currency, - max_delta_days=1): - """implementation of abstract method of Curreny_getter_interface""" - logger = logging.getLogger(__name__) - # we do not want to update the main currency - if main_currency in currency_array: - currency_array.remove(main_currency) - - # Suported currencies - suported = ['MXN', 'USD'] - for curr in currency_array: - if curr in suported: - # Get currency data - main_rate = self.rate_retrieve() - if main_currency == 'MXN': - rate = 1 / main_rate - else: - rate = main_rate - else: - # No other currency supported - continue - - self.updated_currency[curr] = rate - logger.debug("Rate retrieved : %s = %s %s" % - (main_currency, rate, curr)) - - -# CA BOC ##### Bank of Canada ############################################# -class CA_BOC_getter(Curreny_getter_interface): - """Implementation of Curreny_getter_factory interface - for Bank of Canada RSS service - - """ - - def get_updated_currency(self, currency_array, main_currency, - max_delta_days): - """implementation of abstract method of Curreny_getter_interface""" - - # as of Jan 2014 BOC is publishing noon rates for about 60 currencies - url = ('http://www.bankofcanada.ca/stats/assets/' - 'rates_rss/noon/en_%s.xml') - # closing rates are available as well (please note there are only 12 - # currencies reported): - # http://www.bankofcanada.ca/stats/assets/rates_rss/closing/en_%s.xml - - # We do not want to update the main currency - if main_currency in currency_array: - currency_array.remove(main_currency) - - import feedparser - import pytz - from dateutil import parser - - for curr in currency_array: - - _logger.debug("BOC currency rate service : connecting...") - dom = feedparser.parse(url % curr) - - self.validate_cur(curr) - - # check if BOC service is running - if dom.bozo and dom.status != 404: - _logger.error("Bank of Canada - service is down - try again\ - later...") - - # check if BOC sent a valid response for this currency - if dom.status != 200: - _logger.error("Exchange data for %s is not reported by Bank\ - of Canada." % curr) - raise osv.except_osv('Error !', 'Exchange data for %s is not\ - reported by Bank of Canada.' % str(curr)) - - _logger.debug("BOC sent a valid RSS file for: " + curr) - - # check for valid exchange data - if (dom.entries[0].cb_basecurrency == main_currency) and \ - (dom.entries[0].cb_targetcurrency == curr): - rate = dom.entries[0].cb_exchangerate.split('\n', 1)[0] - rate_date_datetime = parser.parse(dom.entries[0].updated)\ - .astimezone(pytz.utc).replace(tzinfo=None) - self.check_rate_date(rate_date_datetime, max_delta_days) - self.updated_currency[curr] = rate - _logger.debug("BOC Rate retrieved : %s = %s %s" % - (main_currency, rate, curr)) - else: - _logger.error( - "Exchange data format error for Bank of Canada -" - "%s. Please check provider data format " - "and/or source code." % curr) - raise osv.except_osv('Error !', - 'Exchange data format error for\ - Bank of Canada - %s !' % str(curr)) - - return self.updated_currency, self.log_info diff --git a/currency_rate_update/services/update_service_CA_BOC.py b/currency_rate_update/services/update_service_CA_BOC.py new file mode 100644 index 000000000..d0e18399a --- /dev/null +++ b/currency_rate_update/services/update_service_CA_BOC.py @@ -0,0 +1,97 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (c) 2009 CamptoCamp. All rights reserved. +# @author Nicolas Bessi +# +# Abstract class to fetch rates from Bank of Canada +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from currency_getter_interface import Currency_getter_interface + +from openerp import _ +from openerp.exceptions import except_orm + +import logging +_logger = logging.getLogger(__name__) + + +class CA_BOC_getter(Currency_getter_interface): + """Implementation of Curreny_getter_factory interface + for Bank of Canada RSS service + + """ + + def get_updated_currency(self, currency_array, main_currency, + max_delta_days): + """implementation of abstract method of Curreny_getter_interface""" + + # as of Jan 2014 BOC is publishing noon rates for about 60 currencies + url = ('http://www.bankofcanada.ca/stats/assets/' + 'rates_rss/noon/en_%s.xml') + # closing rates are available as well (please note there are only 12 + # currencies reported): + # http://www.bankofcanada.ca/stats/assets/rates_rss/closing/en_%s.xml + + # We do not want to update the main currency + if main_currency in currency_array: + currency_array.remove(main_currency) + + import feedparser + import pytz + from dateutil import parser + + for curr in currency_array: + + _logger.debug("BOC currency rate service : connecting...") + dom = feedparser.parse(url % curr) + + self.validate_cur(curr) + + # check if BOC service is running + if dom.bozo and dom.status != 404: + _logger.error("Bank of Canada - service is down - try again\ + later...") + + # check if BOC sent a valid response for this currency + if dom.status != 200: + _logger.error("Exchange data for %s is not reported by Bank\ + of Canada." % curr) + raise except_orm(_('Error !'), _('Exchange data for %s is not\ + reported by Bank of Canada.' % str(curr))) + + _logger.debug("BOC sent a valid RSS file for: " + curr) + + # check for valid exchange data + if (dom.entries[0].cb_basecurrency == main_currency) and \ + (dom.entries[0].cb_targetcurrency == curr): + rate = dom.entries[0].cb_exchangerate.split('\n', 1)[0] + rate_date_datetime = parser.parse(dom.entries[0].updated)\ + .astimezone(pytz.utc).replace(tzinfo=None) + self.check_rate_date(rate_date_datetime, max_delta_days) + self.updated_currency[curr] = rate + _logger.debug("BOC Rate retrieved : %s = %s %s" % + (main_currency, rate, curr)) + else: + _logger.error( + "Exchange data format error for Bank of Canada -" + "%s. Please check provider data format " + "and/or source code." % curr) + raise except_orm(_('Error !'), + _('Exchange data format error for\ + Bank of Canada - %s !' % str(curr))) + + return self.updated_currency, self.log_info diff --git a/currency_rate_update/services/update_service_CH_ADMIN.py b/currency_rate_update/services/update_service_CH_ADMIN.py new file mode 100644 index 000000000..110ad5c5a --- /dev/null +++ b/currency_rate_update/services/update_service_CH_ADMIN.py @@ -0,0 +1,113 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (c) 2009 CamptoCamp. All rights reserved. +# @author Nicolas Bessi +# +# Abstract class to fetch rates from Swiss Federal Authorities +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from currency_getter_interface import Currency_getter_interface + +import logging +_logger = logging.getLogger(__name__) + +from datetime import datetime +from openerp.tools import DEFAULT_SERVER_DATE_FORMAT + + +class CH_ADMIN_getter(Currency_getter_interface): + """Implementation of Currency_getter_factory interface + for Admin.ch service + + """ + + def rate_retrieve(self, dom, ns, curr): + """Parse a dom node to retrieve currencies data""" + res = {} + xpath_rate_currency = ("/def:wechselkurse/def:devise[@code='%s']/" + "def:kurs/text()") % (curr.lower()) + xpath_rate_ref = ("/def:wechselkurse/def:devise[@code='%s']/" + "def:waehrung/text()") % (curr.lower()) + res['rate_currency'] = float( + dom.xpath(xpath_rate_currency, namespaces=ns)[0] + ) + res['rate_ref'] = float( + (dom.xpath(xpath_rate_ref, namespaces=ns)[0]).split(' ')[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.afd.admin.ch/publicdb/newdb/' + 'mwst_kurse/wechselkurse.php') + # We do not want to update the main currency + if main_currency in currency_array: + currency_array.remove(main_currency) + # Move to new XML lib cf Launchpad bug #645263 + from lxml import etree + _logger.debug("Admin.ch currency rate service : connecting...") + rawfile = self.get_url(url) + dom = etree.fromstring(rawfile) + _logger.debug("Admin.ch sent a valid XML file") + adminch_ns = { + 'def': 'http://www.afd.admin.ch/publicdb/newdb/mwst_kurse' + } + rate_date = dom.xpath( + '/def:wechselkurse/def:datum/text()', + namespaces=adminch_ns + ) + rate_date = rate_date[0] + rate_date_datetime = datetime.strptime(rate_date, + DEFAULT_SERVER_DATE_FORMAT) + self.check_rate_date(rate_date_datetime, max_delta_days) + # we dynamically update supported currencies + self.supported_currency_array = dom.xpath( + "/def:wechselkurse/def:devise/@code", + namespaces=adminch_ns + ) + self.supported_currency_array = [x.upper() for x + in self.supported_currency_array] + self.supported_currency_array.append('CHF') + + _logger.debug( + "Supported currencies = " + str(self.supported_currency_array) + ) + self.validate_cur(main_currency) + if main_currency != 'CHF': + main_curr_data = self.rate_retrieve(dom, adminch_ns, main_currency) + # 1 MAIN_CURRENCY = main_rate CHF + rate_curr = main_curr_data['rate_currency'] + rate_ref = main_curr_data['rate_ref'] + main_rate = rate_curr / rate_ref + for curr in currency_array: + self.validate_cur(curr) + if curr == 'CHF': + rate = main_rate + else: + curr_data = self.rate_retrieve(dom, adminch_ns, curr) + # 1 MAIN_CURRENCY = rate CURR + if main_currency == 'CHF': + rate = curr_data['rate_ref'] / curr_data['rate_currency'] + else: + rate = (main_rate * curr_data['rate_ref'] / + 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 diff --git a/currency_rate_update/services/update_service_ECB.py b/currency_rate_update/services/update_service_ECB.py new file mode 100644 index 000000000..ceddce108 --- /dev/null +++ b/currency_rate_update/services/update_service_ECB.py @@ -0,0 +1,103 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (c) 2009 CamptoCamp. All rights reserved. +# @author Nicolas Bessi +# +# Abstract class to fetch rates from European Central Bank +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from currency_getter_interface import Currency_getter_interface + +from datetime import datetime +from openerp.tools import DEFAULT_SERVER_DATE_FORMAT + +import logging +_logger = logging.getLogger(__name__) + + +class ECB_getter(Currency_getter_interface): + """Implementation of Currency_getter_factory interface + for ECB service + """ + + def rate_retrieve(self, dom, ns, curr): + """Parse a dom node to retrieve- + currencies data + + """ + res = {} + xpath_curr_rate = ("/gesmes:Envelope/def:Cube/def:Cube/" + "def:Cube[@currency='%s']/@rate") % (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.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml' + # Important : as explained on the ECB web site, the currencies are + # at the beginning of the afternoon ; so, until 3 p.m. Paris time + # the currency rates are the ones of trading day N-1 + # http://www.ecb.europa.eu/stats/exchange/eurofxref/html/index.en.html + + # We do not want to update the main currency + if main_currency in currency_array: + currency_array.remove(main_currency) + # Move to new XML lib cf Launchpad bug #645263 + from lxml import etree + _logger.debug("ECB currency rate service : connecting...") + rawfile = self.get_url(url) + dom = etree.fromstring(rawfile) + _logger.debug("ECB sent a valid XML file") + ecb_ns = { + 'gesmes': 'http://www.gesmes.org/xml/2002-08-01', + 'def': 'http://www.ecb.int/vocabulary/2002-08-01/eurofxref' + } + rate_date = dom.xpath('/gesmes:Envelope/def:Cube/def:Cube/@time', + namespaces=ecb_ns)[0] + rate_date_datetime = datetime.strptime(rate_date, + DEFAULT_SERVER_DATE_FORMAT) + self.check_rate_date(rate_date_datetime, max_delta_days) + # We dynamically update supported currencies + self.supported_currency_array = dom.xpath( + "/gesmes:Envelope/def:Cube/def:Cube/def:Cube/@currency", + namespaces=ecb_ns + ) + self.supported_currency_array.append('EUR') + _logger.debug("Supported currencies = %s " % + self.supported_currency_array) + self.validate_cur(main_currency) + if main_currency != 'EUR': + main_curr_data = self.rate_retrieve(dom, ecb_ns, main_currency) + for curr in currency_array: + self.validate_cur(curr) + if curr == 'EUR': + rate = 1 / main_curr_data['rate_currency'] + else: + curr_data = self.rate_retrieve(dom, ecb_ns, curr) + if main_currency == 'EUR': + 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 diff --git a/currency_rate_update/services/update_service_MX_BdM.py b/currency_rate_update/services/update_service_MX_BdM.py new file mode 100644 index 000000000..8e3680a18 --- /dev/null +++ b/currency_rate_update/services/update_service_MX_BdM.py @@ -0,0 +1,82 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (c) 2009 CamptoCamp. All rights reserved. +# @author Nicolas Bessi +# +# Abstract class to fetch rates from Banco de México +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from currency_getter_interface import Currency_getter_interface + +import logging +_logger = logging.getLogger(__name__) + + +class MX_BdM_getter(Currency_getter_interface): + """Implementation of Currency_getter_factory interface + for Banco de México service + + """ + + def rate_retrieve(self): + """ Get currency exchange from Banxico.xml and proccess it + TODO: Get correct data from xml instead of process string + """ + url = ('http://www.banxico.org.mx/rsscb/rss?' + 'BMXC_canal=pagos&BMXC_idioma=es') + + from xml.dom.minidom import parse + from StringIO import StringIO + + logger = logging.getLogger(__name__) + logger.debug("Banxico currency rate service : connecting...") + rawfile = self.get_url(url) + + dom = parse(StringIO(rawfile)) + logger.debug("Banxico sent a valid XML file") + + value = dom.getElementsByTagName('cb:value')[0] + rate = value.firstChild.nodeValue + + return float(rate) + + def get_updated_currency(self, currency_array, main_currency, + max_delta_days=1): + """implementation of abstract method of Curreny_getter_interface""" + logger = logging.getLogger(__name__) + # we do not want to update the main currency + if main_currency in currency_array: + currency_array.remove(main_currency) + + # Suported currencies + suported = ['MXN', 'USD'] + for curr in currency_array: + if curr in suported: + # Get currency data + main_rate = self.rate_retrieve() + if main_currency == 'MXN': + rate = 1 / main_rate + else: + rate = main_rate + else: + # No other currency supported + continue + + self.updated_currency[curr] = rate + logger.debug("Rate retrieved : %s = %s %s" % + (main_currency, rate, curr)) diff --git a/currency_rate_update/services/update_service_PL_NBP.py b/currency_rate_update/services/update_service_PL_NBP.py new file mode 100644 index 000000000..0f57cc265 --- /dev/null +++ b/currency_rate_update/services/update_service_PL_NBP.py @@ -0,0 +1,101 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (c) 2009 CamptoCamp. All rights reserved. +# @author Nicolas Bessi +# +# Abstract class to fetch rates from National Bank of Poland +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from currency_getter_interface import Currency_getter_interface + +from datetime import datetime +from openerp.tools import DEFAULT_SERVER_DATE_FORMAT + +import logging +_logger = logging.getLogger(__name__) + + +class PL_NBP_getter(Currency_getter_interface): + """Implementation of Currency_getter_factory interface + for PL NBP service + + """ + + def rate_retrieve(self, dom, ns, curr): + """ Parse a dom node to retrieve + currencies data""" + res = {} + xpath_rate_currency = ("/tabela_kursow/pozycja[kod_waluty='%s']/" + "kurs_sredni/text()") % (curr.upper()) + xpath_rate_ref = ("/tabela_kursow/pozycja[kod_waluty='%s']/" + "przelicznik/text()") % (curr.upper()) + res['rate_currency'] = float( + dom.xpath(xpath_rate_currency, namespaces=ns)[0].replace(',', '.') + ) + res['rate_ref'] = float(dom.xpath(xpath_rate_ref, 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""" + # LastA.xml is always the most recent one + url = 'http://www.nbp.pl/kursy/xml/LastA.xml' + # We do not want to update the main currency + if main_currency in currency_array: + currency_array.remove(main_currency) + # Move to new XML lib cf Launchpad bug #645263 + from lxml import etree + _logger.debug("NBP.pl currency rate service : connecting...") + rawfile = self.get_url(url) + dom = etree.fromstring(rawfile) + ns = {} # Cool, there are no namespaces ! + _logger.debug("NBP.pl sent a valid XML file") + rate_date = dom.xpath('/tabela_kursow/data_publikacji/text()', + namespaces=ns)[0] + rate_date_datetime = datetime.strptime(rate_date, + DEFAULT_SERVER_DATE_FORMAT) + self.check_rate_date(rate_date_datetime, max_delta_days) + # We dynamically update supported currencies + self.supported_currency_array = dom.xpath( + '/tabela_kursow/pozycja/kod_waluty/text()', + namespaces=ns + ) + self.supported_currency_array.append('PLN') + _logger.debug("Supported currencies = %s" % + self.supported_currency_array) + self.validate_cur(main_currency) + if main_currency != 'PLN': + main_curr_data = self.rate_retrieve(dom, ns, main_currency) + # 1 MAIN_CURRENCY = main_rate PLN + main_rate = (main_curr_data['rate_currency'] / + main_curr_data['rate_ref']) + for curr in currency_array: + self.validate_cur(curr) + if curr == 'PLN': + rate = main_rate + else: + curr_data = self.rate_retrieve(dom, ns, curr) + # 1 MAIN_CURRENCY = rate CURR + if main_currency == 'PLN': + rate = curr_data['rate_ref'] / curr_data['rate_currency'] + else: + rate = (main_rate * curr_data['rate_ref'] / + curr_data['rate_currency']) + self.updated_currency[curr] = rate + _logger.debug("Rate retrieved : %s = %s %s" % + (main_currency, rate, curr)) + return self.updated_currency, self.log_info diff --git a/currency_rate_update/services/update_service_RO_BNR.py b/currency_rate_update/services/update_service_RO_BNR.py new file mode 100644 index 000000000..468a8f563 --- /dev/null +++ b/currency_rate_update/services/update_service_RO_BNR.py @@ -0,0 +1,99 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (c) 2009 CamptoCamp. All rights reserved. +# @author Nicolas Bessi +# +# Abstract class to fetch rates from National Bank of Romania +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from currency_getter_interface import Currency_getter_interface + +from datetime import datetime, timedelta + +import logging +_logger = logging.getLogger(__name__) + + +class RO_BNR_getter(Currency_getter_interface): + """Implementation of Currency_getter_factory interface for BNR service""" + + def rate_retrieve(self, dom, ns, curr): + """ Parse a dom node to retrieve- + currencies data""" + res = {} + xpath_rate_currency = "/def:DataSet/def:Body/def:Cube/def:Rate" + \ + "[@currency='%s']/text()" % (curr.upper()) + xpath_rate_ref = "/def:DataSet/def:Body/def:Cube/def:Rate" + \ + "[@currency='%s']/@multiplier" % (curr.upper()) + res['rate_currency'] = float(dom.xpath(xpath_rate_currency, + namespaces=ns)[0]) + try: + res['rate_ref'] = float(dom.xpath(xpath_rate_ref, + namespaces=ns)[0]) + except: + res['rate_ref'] = 1 + 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.bnr.ro/nbrfxrates.xml' + # we do not want to update the main currency + if main_currency in currency_array: + currency_array.remove(main_currency) + # Move to new XML lib cf Launchpad bug #645263 + from lxml import etree + _logger.debug("BNR currency rate service : connecting...") + rawfile = self.get_url(url) + dom = etree.fromstring(rawfile) + adminch_ns = {'def': 'http://www.bnr.ro/xsd'} + rate_date = dom.xpath('/def:DataSet/def:Body/def:Cube/@date', + namespaces=adminch_ns)[0] + rate_date_datetime = datetime.strptime(rate_date, + '%Y-%m-%d') + \ + timedelta(days=1) + self.check_rate_date(rate_date_datetime, max_delta_days) + # we dynamically update supported currencies + self.supported_currency_array = dom.xpath("/def:DataSet/def:Body/" + \ + "def:Cube/def:Rate/@currency", + namespaces=adminch_ns) + self.supported_currency_array = [x.upper() for x in \ + self.supported_currency_array] + self.supported_currency_array.append('RON') + + self.validate_cur(main_currency) + if main_currency != 'RON': + main_curr_data = self.rate_retrieve(dom, adminch_ns, main_currency) + # 1 MAIN_CURRENCY = main_rate RON + main_rate = main_curr_data['rate_currency'] / \ + main_curr_data['rate_ref'] + for curr in currency_array: + self.validate_cur(curr) + if curr == 'RON': + rate = main_rate + else: + curr_data = self.rate_retrieve(dom, adminch_ns, curr) + # 1 MAIN_CURRENCY = rate CURR + if main_currency == 'RON': + rate = curr_data['rate_ref'] / curr_data['rate_currency'] + else: + rate = main_rate * curr_data['rate_ref'] / \ + curr_data['rate_currency'] + self.updated_currency[curr] = rate + _logger.debug("BNR Rate retrieved : 1 " + main_currency + + ' = ' + str(rate) + ' ' + curr) + return self.updated_currency, self.log_info diff --git a/currency_rate_update/services/update_service_YAHOO.py b/currency_rate_update/services/update_service_YAHOO.py new file mode 100644 index 000000000..7ed18a954 --- /dev/null +++ b/currency_rate_update/services/update_service_YAHOO.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (c) 2009 CamptoCamp. All rights reserved. +# @author Nicolas Bessi +# +# Abstract class to fetch rates from Yahoo Financial +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from currency_getter_interface import Currency_getter_interface + + +class YAHOO_getter(Currency_getter_interface): + """Implementation of Currency_getter_factory interface + for Yahoo finance service + """ + + def get_updated_currency(self, currency_array, main_currency, + max_delta_days): + """implementation of abstract method of curreny_getter_interface""" + self.validate_cur(main_currency) + url = ('http://download.finance.yahoo.com/d/' + 'quotes.txt?s="%s"=X&f=sl1c1abg') + if main_currency in currency_array: + currency_array.remove(main_currency) + for curr in currency_array: + self.validate_cur(curr) + res = self.get_url(url % (main_currency + curr)) + val = res.split(',')[1] + if val: + self.updated_currency[curr] = val + else: + raise Exception('Could not update the %s' % (curr)) + + return self.updated_currency, self.log_info From d76addfd7a7da350e1d28369d11015ef578dbc47 Mon Sep 17 00:00:00 2001 From: Fekete Mihai Date: Mon, 10 Nov 2014 13:09:21 +0200 Subject: [PATCH 11/32] Remove self.ensure_one from @api.one. --- currency_rate_update/model/currency_rate_update.py | 1 - 1 file changed, 1 deletion(-) diff --git a/currency_rate_update/model/currency_rate_update.py b/currency_rate_update/model/currency_rate_update.py index e49677161..e1cd0210f 100644 --- a/currency_rate_update/model/currency_rate_update.py +++ b/currency_rate_update/model/currency_rate_update.py @@ -213,7 +213,6 @@ class Currency_rate_update_service(models.Model): @api.one def refresh_currency(self): """Refresh the currencies rates !!for all companies now""" - self.ensure_one() factory = Currency_getter_factory() curr_obj = self.env['res.currency'] rate_obj = self.env['res.currency.rate'] From 36dcab5bdc6ff03b74087762bb014d2a8f3e647d Mon Sep 17 00:00:00 2001 From: Fekete Mihai Date: Mon, 10 Nov 2014 13:16:28 +0200 Subject: [PATCH 12/32] Modify contribuitors. --- currency_rate_update/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/currency_rate_update/README.md b/currency_rate_update/README.md index e119d916f..0544b0b92 100644 --- a/currency_rate_update/README.md +++ b/currency_rate_update/README.md @@ -52,9 +52,11 @@ If in multi-company mode, the base currency will be the first company's currency found in database. Thanks to main contributors: +* Joel Grand-Guillaume +* JB Aubort * Grzegorz Grzelak - grzegorz.grzelak@birdglobe.com (ECB, NBP) * Alexis de Lattre - alexis@via.ecp.fr -* Joel Grand-Guillaume * Lorenzo Battistini - lorenzo.battistini@agilebg.com (Port to V7) * Agustin Cruz - openpyme.mx (BdM) * Dorin Hongu - dhongu@gmail.com (BNR) +* Fekete Mihai - feketemihai@gmail.com (Port to V8) From 805a17c4390238279718c78684989c63e9b0a317 Mon Sep 17 00:00:00 2001 From: Fekete Mihai Date: Mon, 10 Nov 2014 14:04:55 +0200 Subject: [PATCH 13/32] Update acces rights. --- .../security/ir.model.access.csv | 3 + currency_rate_update/security/security.xml | 67 ------------------- 2 files changed, 3 insertions(+), 67 deletions(-) create mode 100644 currency_rate_update/security/ir.model.access.csv delete mode 100644 currency_rate_update/security/security.xml diff --git a/currency_rate_update/security/ir.model.access.csv b/currency_rate_update/security/ir.model.access.csv new file mode 100644 index 000000000..b623fefba --- /dev/null +++ b/currency_rate_update/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_account_currency_rate_update_service_settings,currency.rate.update.service,model_currency_rate_update_service,account.group_account_manager,1,1,1,1 +access_account_currency_rate_update_service_financial,currency.rate.update.service,model_currency_rate_update_service,base.group_system,1,1,1,1 diff --git a/currency_rate_update/security/security.xml b/currency_rate_update/security/security.xml deleted file mode 100644 index 57ae7e316..000000000 --- a/currency_rate_update/security/security.xml +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 0962a7124049b13912bfe270677e6019f396fc5a Mon Sep 17 00:00:00 2001 From: Fekete Mihai Date: Mon, 10 Nov 2014 14:06:36 +0200 Subject: [PATCH 14/32] Update openerp.py security file. --- currency_rate_update/__openerp__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/currency_rate_update/__openerp__.py b/currency_rate_update/__openerp__.py index 618b5977f..a76020e10 100644 --- a/currency_rate_update/__openerp__.py +++ b/currency_rate_update/__openerp__.py @@ -32,7 +32,7 @@ "view/service_cron_data.xml", "view/currency_rate_update.xml", "view/company_view.xml", - "security/security.xml", + "security/ir.model.access.csv", ], "demo": [], "active": False, From 2901bfb0a51497671a7cebb46d965a15c4d1e1ad Mon Sep 17 00:00:00 2001 From: Fekete Mihai Date: Mon, 10 Nov 2014 14:36:02 +0200 Subject: [PATCH 15/32] Fix some flake8, imports not used. --- currency_rate_update/model/company.py | 4 +- .../model/currency_rate_update.py | 95 ++++++++++--------- .../services/currency_getter.py | 8 -- .../services/currency_getter_interface.py | 10 +- 4 files changed, 58 insertions(+), 59 deletions(-) diff --git a/currency_rate_update/model/company.py b/currency_rate_update/model/company.py index a5f7033af..e012be7bb 100644 --- a/currency_rate_update/model/company.py +++ b/currency_rate_update/model/company.py @@ -19,7 +19,7 @@ # ############################################################################## -from openerp import models, fields, api, _ +from openerp import models, fields, api class res_company(models.Model): @@ -52,7 +52,7 @@ class res_company(models.Model): string="Multi company currency", compute="_compute_multi_curr_enable", help="When this option is unchecked it will allow users " - "to set a distinct currency updates on each company." + "to set a distinct currency updates on each company." ) # List of services to fetch rates services_to_use = fields.One2many( diff --git a/currency_rate_update/model/currency_rate_update.py b/currency_rate_update/model/currency_rate_update.py index e1cd0210f..ccdc64de0 100644 --- a/currency_rate_update/model/currency_rate_update.py +++ b/currency_rate_update/model/currency_rate_update.py @@ -25,7 +25,7 @@ from datetime import datetime from dateutil.relativedelta import relativedelta from openerp import models, fields, api, _ -from openerp.exceptions import except_orm, Warning +from openerp.exceptions import Warning from ..services.currency_getter import Currency_getter_factory @@ -58,47 +58,50 @@ supported_currency_array = [ "ZWD" ] -RO_BNR_supported_currency_array = ["AED", "AUD", "BGN", "BRL", "CAD", "CHF", - "CNY", "CZK", "DKK", "EGP", "EUR", "GBP", "HUF", "INR", "JPY", "KRW", - "MDL", "MXN", "NOK", "NZD", "PLN", "RON", "RSD", "RUB", "SEK", "TRY", - "UAH", "USD", "XAU", "XDR", "ZAR"] - -CA_BOC_supported_currency_array = ["AED", "ANG", "ARS", "AUD", "BOC", "BRL", - "BSD", "CHF", "CLP", "CNY", "COP", "CZK", "DKK", "EUR", "FJD", "GBP", - "GHS", "GTQ", "HKD", "HNL", "HRK", "HUF", "IDR", "ILS", "INR", "ISK", - "JMD", "JPY", "KRW", "LKR", "MAD", "MMK", "MXN", "MYR", "NOK", "NZD", - "PAB", "PEN", "PHP", "PKR", "PLN", "RON", "RSD", "RUB", "SEK", "SGD", - "THB", "TND", "TRY", "TTD", "TWD", "USD", "VEF", "VND", "XAF", "XCD", - "XPF", "ZAR"] - -CH_ADMIN_supported_currency_array = ['AED', 'ALL', 'ARS', 'AUD', 'AZN', 'BAM', - 'BDT', 'BGN', 'BHD', 'BRL', 'CAD', 'CHF', 'CLP', 'CNY', 'COP', 'CRC', - 'CZK', 'DKK', 'DOP', 'EGP', 'ETB', 'EUR', 'GBP', 'GTQ', 'HKD', 'HNL', - 'HRK', 'HUF', 'IDR', 'ILS', 'INR', 'ISK', 'JPY', 'KES', 'KHR', 'KRW', - 'KWD', 'KYD', 'KZT', 'LBP', 'LKR', 'LTL', 'LVL', 'LYD', 'MAD', 'MUR', - 'MXN', 'MYR', 'NGN', 'NOK', 'NZD', 'OMR', 'PAB', 'PEN', 'PHP', 'PKR', - 'PLN', 'QAR', 'RON', 'RSD', 'RUB', 'SAR', 'SEK', 'SGD', 'THB', 'TND', - 'TRY', 'TWD', 'TZS', 'UAH', 'USD', 'UYU', 'VEF', 'VND', 'ZAR'] - -ECB_supported_currency_array = ['AUD', 'BGN', 'BRL', 'CAD', 'CHF', 'CNY', - 'CZK', 'DKK', 'GBP', 'HKD', 'HRK', 'HUF', 'IDR', 'ILS', 'INR', 'JPY', - 'KRW', 'LTL', 'MXN', 'MYR', 'NOK', 'NZD', 'PHP', 'PLN', 'RON', 'RUB', - 'SEK', 'SGD', 'THB', 'TRY', 'USD', 'ZAR'] - -MX_BdM_supported_currency_array = ["ARS", "AUD", "BBD", "BMD", "BOB", "BRL", - "BSD", "BZD", "CAD", "CHF", "CLP", "CNH", "CNY", "COP", "CRC", "CUP", - "CZK", "DKK", "DOP", "DZD", "EGP", "ESD", "EUR", "FJD", "GBP", "GTQ", - "GYD", "HKD", "HNL", "HUF", "IDR", "ILS", "INR", "IQD", "JMD", "JPY", - "KES", "KRW", "KWD", "MAD", "MYR", "NGN", "NIC", "NOK", "NZD", "PAB", - "PEN", "PHP", "PLN", "PYG", "RON", "RUB", "SAR", "SEK", "SGD", "SVC", - "THB", "TRY", "TTD", "TWD", "UAH", "USD", "USD", "UYP", "VEF", "VND", +RO_BNR_supported_currency_array = [ + "AED", "AUD", "BGN", "BRL", "CAD", "CHF", "CNY", "CZK", "DKK", "EGP", + "EUR", "GBP", "HUF", "INR", "JPY", "KRW", "MDL", "MXN", "NOK", "NZD", + "PLN", "RON", "RSD", "RUB", "SEK", "TRY", "UAH", "USD", "XAU", "XDR", "ZAR"] -PL_NBP_supported_currency_array = ['AUD', 'BGN', 'BRL', 'CAD', 'CHF', 'CLP', - 'CNY', 'CZK', 'DKK', 'EUR', 'GBP', 'HKD', 'HRK', 'HUF', 'IDR', 'ILS', - 'INR', 'ISK', 'JPY', 'KRW', 'LTL', 'MXN', 'MYR', 'NOK', 'NZD', 'PHP', - 'PLN', 'RON', 'RUB', 'SEK', 'SGD', 'THB', 'TRY', 'UAH', 'USD', 'XDR', - 'ZAR'] +CA_BOC_supported_currency_array = [ + "AED", "ANG", "ARS", "AUD", "BOC", "BRL", "BSD", "CHF", "CLP", "CNY", + "COP", "CZK", "DKK", "EUR", "FJD", "GBP", "GHS", "GTQ", "HKD", "HNL", + "HRK", "HUF", "IDR", "ILS", "INR", "ISK", "JMD", "JPY", "KRW", "LKR", + "MAD", "MMK", "MXN", "MYR", "NOK", "NZD", "PAB", "PEN", "PHP", "PKR", + "PLN", "RON", "RSD", "RUB", "SEK", "SGD", "THB", "TND", "TRY", "TTD", + "TWD", "USD", "VEF", "VND", "XAF", "XCD", "XPF", "ZAR"] + +CH_ADMIN_supported_currency_array = [ + "AED", "ALL", "ARS", "AUD", "AZN", "BAM", "BDT", "BGN", "BHD", "BRL", + "CAD", "CHF", "CLP", "CNY", "COP", "CRC", "CZK", "DKK", "DOP", "EGP", + "ETB", "EUR", "GBP", "GTQ", "HKD", "HNL", "HRK", "HUF", "IDR", "ILS", + "INR", "ISK", "JPY", "KES", "KHR", "KRW", "KWD", "KYD", "KZT", "LBP", + "LKR", "LTL", "LVL", "LYD", "MAD", "MUR", "MXN", "MYR", "NGN", "NOK", + "NZD", "OMR", "PAB", "PEN", "PHP", "PKR", "PLN", "QAR", "RON", "RSD", + "RUB", "SAR", "SEK", "SGD", "THB", "TND", "TRY", "TWD", "TZS", "UAH", + "USD", "UYU", "VEF", "VND", "ZAR"] + +ECB_supported_currency_array = [ + "AUD", "BGN", "BRL", "CAD", "CHF", "CNY", "CZK", "DKK", "GBP", "HKD", + "HRK", "HUF", "IDR", "ILS", "INR", "JPY", "KRW", "LTL", "MXN", "MYR", + "NOK", "NZD", "PHP", "PLN", "RON", "RUB", "SEK", "SGD", "THB", "TRY", + "USD", "ZAR"] + +MX_BdM_supported_currency_array = [ + "ARS", "AUD", "BBD", "BMD", "BOB", "BRL", "BSD", "BZD", "CAD", "CHF", + "CLP", "CNH", "CNY", "COP", "CRC", "CUP", "CZK", "DKK", "DOP", "DZD", + "EGP", "ESD", "EUR", "FJD", "GBP", "GTQ", "GYD", "HKD", "HNL", "HUF", + "IDR", "ILS", "INR", "IQD", "JMD", "JPY", "KES", "KRW", "KWD", "MAD", + "MYR", "NGN", "NIC", "NOK", "NZD", "PAB", "PEN", "PHP", "PLN", "PYG", + "RON", "RUB", "SAR", "SEK", "SGD", "SVC", "THB", "TRY", "TTD", "TWD", + "UAH", "USD", "USD", "UYP", "VEF", "VND", "ZAR"] + +PL_NBP_supported_currency_array = [ + "AUD", "BGN", "BRL", "CAD", "CHF", "CLP", "CNY", "CZK", "DKK", "EUR", + "GBP", "HKD", "HRK", "HUF", "IDR", "ILS", "INR", "ISK", "JPY", "KRW", + "LTL", "MXN", "MYR", "NOK", "NZD", "PHP", "PLN", "RON", "RUB", "SEK", + "SGD", "THB", "TRY", "UAH", "USD", "XDR", "ZAR"] class Currency_rate_update_service(models.Model): @@ -118,12 +121,13 @@ class Currency_rate_update_service(models.Model): def _check_interval_number(self): if self.interval_number < 0: raise Warning(_('Interval number must be >= 0')) - + @api.onchange('interval_number') def _onchange_interval_number(self): if self.interval_number == 0: - self.note = "%s Service deactivated. Currencies will no longer be updated. \n%s" % ( - fields.Datetime.now(), self.note and self.note or '') + self.note = "%s Service deactivated. Currencies will no longer " + "be updated. \n%s" % (fields.Datetime.now(), + self.note and self.note or '') @api.onchange('service') def _onchange_service(self): @@ -245,8 +249,9 @@ class Currency_rate_update_service(models.Model): main_curr.name, self.max_delta_days ) - rate_name = fields.Datetime.to_string(datetime.utcnow().replace( - hour=0, minute=0, second=0, microsecond=0)) + rate_name = fields.Datetime.to_string( + datetime.utcnow().replace(hour=0, minute=0, + second=0, microsecond=0)) for curr in self.currency_to_update: if curr.id == main_curr.id: continue diff --git a/currency_rate_update/services/currency_getter.py b/currency_rate_update/services/currency_getter.py index bc5cf5a51..d0e228e31 100644 --- a/currency_rate_update/services/currency_getter.py +++ b/currency_rate_update/services/currency_getter.py @@ -19,14 +19,6 @@ # ############################################################################## -from update_service_ECB import ECB_getter -from update_service_YAHOO import YAHOO_getter - -from update_service_CA_BOC import CA_BOC_getter -from update_service_CH_ADMIN import CH_ADMIN_getter -from update_service_MX_BdM import MX_BdM_getter -from update_service_PL_NBP import PL_NBP_getter -from update_service_RO_BNR import RO_BNR_getter class AbstractClassError(Exception): def __str__(self): diff --git a/currency_rate_update/services/currency_getter_interface.py b/currency_rate_update/services/currency_getter_interface.py index 712507d09..133f685b9 100644 --- a/currency_rate_update/services/currency_getter_interface.py +++ b/currency_rate_update/services/currency_getter_interface.py @@ -27,6 +27,7 @@ from openerp.exceptions import except_orm _logger = logging.getLogger(__name__) + class AbstractClassError(Exception): def __str__(self): return 'Abstract Class' @@ -60,7 +61,8 @@ class UnsuportedCurrencyError(Exception): def __repr__(self): return 'Unsupported currency %s' % self.curr - + + class Currency_getter_interface(object): "Abstract class of currency getter" @@ -135,8 +137,8 @@ class Currency_getter_interface(object): # We always have a warning when rate_date != today if rate_date.date() != datetime.today().date(): - rate_date_str = fields.Date.to_string(rate_date) - msg = "The rate timestamp %s is not today's date %s" % ( - rate_date_str, fields.Date.today()) + rate_date_str = fields.Date.to_string(rate_date) + msg = "The rate timestamp %s is not today's date %s" % + (rate_date_str, fields.Date.today()) self.log_info = ("\n WARNING : %s") % msg _logger.warning(msg) From f62fc0a4a69cff8eaace925d3ca867e0f543e0e2 Mon Sep 17 00:00:00 2001 From: Fekete Mihai Date: Mon, 10 Nov 2014 14:54:50 +0200 Subject: [PATCH 16/32] Fix flake8. --- .../model/currency_rate_update.py | 86 ++++++++++--------- .../services/currency_getter_interface.py | 4 +- .../services/update_service_CA_BOC.py | 4 +- .../services/update_service_RO_BNR.py | 23 +++-- 4 files changed, 59 insertions(+), 58 deletions(-) diff --git a/currency_rate_update/model/currency_rate_update.py b/currency_rate_update/model/currency_rate_update.py index ccdc64de0..500d34ad5 100644 --- a/currency_rate_update/model/currency_rate_update.py +++ b/currency_rate_update/model/currency_rate_update.py @@ -125,9 +125,9 @@ class Currency_rate_update_service(models.Model): @api.onchange('interval_number') def _onchange_interval_number(self): if self.interval_number == 0: - self.note = "%s Service deactivated. Currencies will no longer " + self.note = "%s Service deactivated. Currencies will no longer " \ "be updated. \n%s" % (fields.Datetime.now(), - self.note and self.note or '') + self.note and self.note or '') @api.onchange('service') def _onchange_service(self): @@ -153,33 +153,33 @@ class Currency_rate_update_service(models.Model): if company_id: currencies = self.env['res.currency'].search( [('name', 'in', currency_list), - '|', ('company_id', '=', company_id), - ('company_id', '=', False)]) + '|', ('company_id', '=', company_id), + ('company_id', '=', False)]) else: currencies = self.env['res.currency'].search( [('name', 'in', currency_list), - ('company_id', '=', False)]) + ('company_id', '=', False)]) self.currency_list = [(6, 0, [curr.id for curr in currencies])] # List of webservicies the value sould be a class name - service = fields.Selection([ - ('CH_ADMIN_getter', 'Admin.ch'), - ('ECB_getter', 'European Central Bank'), - ('YAHOO_getter', 'Yahoo Finance'), - # Added for polish rates - ('PL_NBP_getter', 'Narodowy Bank Polski'), - # Added for mexican rates - ('MX_BdM_getter', 'Banco de México'), - # Bank of Canada is using RSS-CB - # http://www.cbwiki.net/wiki/index.php/Specification_1.1 - # This RSS format is used by other national banks - # (Thailand, Malaysia, Mexico...) - ('CA_BOC_getter', 'Bank of Canada - noon rates'), - # Added for romanian rates - ('RO_BNR_getter', 'National Bank of Romania') - ], - "Webservice to use", - required=True) + service = fields.Selection( + [('CH_ADMIN_getter', 'Admin.ch'), + ('ECB_getter', 'European Central Bank'), + ('YAHOO_getter', 'Yahoo Finance'), + # Added for polish rates + ('PL_NBP_getter', 'Narodowy Bank Polski'), + # Added for mexican rates + ('MX_BdM_getter', 'Banco de México'), + # Bank of Canada is using RSS-CB + # http://www.cbwiki.net/wiki/index.php/Specification_1.1 + # This RSS format is used by other national banks + # (Thailand, Malaysia, Mexico...) + ('CA_BOC_getter', 'Bank of Canada - noon rates'), + # Added for romanian rates + ('RO_BNR_getter', 'National Bank of Romania') + ], + "Webservice to use", + required=True) # List of currencies available on webservice currency_list = fields.Many2many('res.currency', 'res_currency_update_avail_rel', @@ -187,21 +187,21 @@ class Currency_rate_update_service(models.Model): 'currency_id', 'Currencies available') # List of currency to update - currency_to_update = fields.Many2many( - 'res.currency', - 'res_currency_auto_update_rel', - 'service_id', - 'currency_id', - 'Currencies to update with this service') + currency_to_update = fields.Many2many('res.currency', + 'res_currency_auto_update_rel', + 'service_id', + 'currency_id', + 'Currencies to update with this ' + 'service') # Link with company company_id = fields.Many2one('res.company', 'Linked Company') # Note fileds that will be used as a logger note = fields.Text('Update notice') - max_delta_days = fields.Integer('Max delta days', - 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 OpenERP.") + max_delta_days = fields.Integer( + 'Max delta days', 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 OpenERP.") interval_type = fields.Selection([ ('days', 'Day(s)'), ('weeks', 'Week(s)'), @@ -211,8 +211,10 @@ class Currency_rate_update_service(models.Model): interval_number = fields.Integer('Frequency', default=1) next_run = fields.Date('Next run on', default=fields.Date.today()) - _sql_constraints = [('curr_service_unique', 'unique (service, company_id)', - _('You can use a service only one time per company !'))] + _sql_constraints = [('curr_service_unique', + 'unique (service, company_id)', + _('You can use a service only one time per ' + 'company !'))] @api.one def refresh_currency(self): @@ -243,22 +245,22 @@ class Currency_rate_update_service(models.Model): # and return a dict of rate getter = factory.register(self.service) curr_to_fetch = map(lambda x: x.name, - self.currency_to_update) + self.currency_to_update) res, log_info = getter.get_updated_currency( curr_to_fetch, main_curr.name, self.max_delta_days ) - rate_name = fields.Datetime.to_string( - datetime.utcnow().replace(hour=0, minute=0, - second=0, microsecond=0)) + rate_name = \ + fields.Datetime.to_string(datetime.utcnow().replace( + hour=0, minute=0, second=0, microsecond=0)) for curr in self.currency_to_update: if curr.id == main_curr.id: continue do_create = True for rate in curr.rate_ids: if rate.name == rate_name: - rate.rate= res[curr.name] + rate.rate = res[curr.name] do_create = False break if do_create: @@ -275,7 +277,7 @@ class Currency_rate_update_service(models.Model): fields.Datetime.to_string(datetime.today()), note ) - self.write({'note': msg}) + self.write({'note': msg}) except Exception as exc: error_msg = "\n%s ERROR : %s %s" % ( fields.Datetime.to_string(datetime.today()), diff --git a/currency_rate_update/services/currency_getter_interface.py b/currency_rate_update/services/currency_getter_interface.py index 133f685b9..fc2b045bb 100644 --- a/currency_rate_update/services/currency_getter_interface.py +++ b/currency_rate_update/services/currency_getter_interface.py @@ -138,7 +138,7 @@ class Currency_getter_interface(object): # We always have a warning when rate_date != today if rate_date.date() != datetime.today().date(): rate_date_str = fields.Date.to_string(rate_date) - msg = "The rate timestamp %s is not today's date %s" % - (rate_date_str, fields.Date.today()) + msg = "The rate timestamp %s is not today's date %s" % \ + (rate_date_str, fields.Date.today()) self.log_info = ("\n WARNING : %s") % msg _logger.warning(msg) diff --git a/currency_rate_update/services/update_service_CA_BOC.py b/currency_rate_update/services/update_service_CA_BOC.py index d0e18399a..80f53c819 100644 --- a/currency_rate_update/services/update_service_CA_BOC.py +++ b/currency_rate_update/services/update_service_CA_BOC.py @@ -91,7 +91,7 @@ class CA_BOC_getter(Currency_getter_interface): "%s. Please check provider data format " "and/or source code." % curr) raise except_orm(_('Error !'), - _('Exchange data format error for\ - Bank of Canada - %s !' % str(curr))) + _('Exchange data format error for\ + Bank of Canada - %s !' % str(curr))) return self.updated_currency, self.log_info diff --git a/currency_rate_update/services/update_service_RO_BNR.py b/currency_rate_update/services/update_service_RO_BNR.py index 468a8f563..2427c4426 100644 --- a/currency_rate_update/services/update_service_RO_BNR.py +++ b/currency_rate_update/services/update_service_RO_BNR.py @@ -63,16 +63,15 @@ class RO_BNR_getter(Currency_getter_interface): adminch_ns = {'def': 'http://www.bnr.ro/xsd'} rate_date = dom.xpath('/def:DataSet/def:Body/def:Cube/@date', namespaces=adminch_ns)[0] - rate_date_datetime = datetime.strptime(rate_date, - '%Y-%m-%d') + \ - timedelta(days=1) + rate_date_datetime = datetime.strptime(rate_date, '%Y-%m-%d') + \ + timedelta(days=1) self.check_rate_date(rate_date_datetime, max_delta_days) # we dynamically update supported currencies - self.supported_currency_array = dom.xpath("/def:DataSet/def:Body/" + \ - "def:Cube/def:Rate/@currency", - namespaces=adminch_ns) - self.supported_currency_array = [x.upper() for x in \ - self.supported_currency_array] + self.supported_currency_array = dom.xpath( + "/def:DataSet/def:Body/" + "def:Cube/def:Rate/@currency", + namespaces=adminch_ns) + self.supported_currency_array = [ + x.upper() for x in self.supported_currency_array] self.supported_currency_array.append('RON') self.validate_cur(main_currency) @@ -80,7 +79,7 @@ class RO_BNR_getter(Currency_getter_interface): main_curr_data = self.rate_retrieve(dom, adminch_ns, main_currency) # 1 MAIN_CURRENCY = main_rate RON main_rate = main_curr_data['rate_currency'] / \ - main_curr_data['rate_ref'] + main_curr_data['rate_ref'] for curr in currency_array: self.validate_cur(curr) if curr == 'RON': @@ -92,8 +91,8 @@ class RO_BNR_getter(Currency_getter_interface): rate = curr_data['rate_ref'] / curr_data['rate_currency'] else: rate = main_rate * curr_data['rate_ref'] / \ - curr_data['rate_currency'] + curr_data['rate_currency'] self.updated_currency[curr] = rate - _logger.debug("BNR Rate retrieved : 1 " + main_currency + - ' = ' + str(rate) + ' ' + curr) + _logger.debug("BNR Rate retrieved : 1 " + main_currency + ' = ' + + str(rate) + ' ' + curr) return self.updated_currency, self.log_info From 722f8b1a805748ed1e944eb2cbf22d4de8999b22 Mon Sep 17 00:00:00 2001 From: Fekete Mihai Date: Mon, 10 Nov 2014 15:00:48 +0200 Subject: [PATCH 17/32] Re-add imports of services abstract classes. --- currency_rate_update/services/currency_getter.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/currency_rate_update/services/currency_getter.py b/currency_rate_update/services/currency_getter.py index d0e228e31..598eccfba 100644 --- a/currency_rate_update/services/currency_getter.py +++ b/currency_rate_update/services/currency_getter.py @@ -19,6 +19,15 @@ # ############################################################################## +from update_service_ECB import ECB_getter +from update_service_YAHOO import YAHOO_getter + +from update_service_CA_BOC import CA_BOC_getter +from update_service_CH_ADMIN import CH_ADMIN_getter +from update_service_MX_BdM import MX_BdM_getter +from update_service_PL_NBP import PL_NBP_getter +from update_service_RO_BNR import RO_BNR_getter + class AbstractClassError(Exception): def __str__(self): From 37a5526e46266b901222250cb5479487037f3e13 Mon Sep 17 00:00:00 2001 From: Fekete Mihai Date: Mon, 10 Nov 2014 15:52:49 +0200 Subject: [PATCH 18/32] Add dynamic import of services to pass flake8. --- currency_rate_update/services/currency_getter.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/currency_rate_update/services/currency_getter.py b/currency_rate_update/services/currency_getter.py index 598eccfba..495d0477b 100644 --- a/currency_rate_update/services/currency_getter.py +++ b/currency_rate_update/services/currency_getter.py @@ -19,15 +19,6 @@ # ############################################################################## -from update_service_ECB import ECB_getter -from update_service_YAHOO import YAHOO_getter - -from update_service_CA_BOC import CA_BOC_getter -from update_service_CH_ADMIN import CH_ADMIN_getter -from update_service_MX_BdM import MX_BdM_getter -from update_service_PL_NBP import PL_NBP_getter -from update_service_RO_BNR import RO_BNR_getter - class AbstractClassError(Exception): def __str__(self): @@ -82,6 +73,8 @@ class Currency_getter_factory(): 'RO_BNR_getter', ] if class_name in allowed: + exec "from update_service_%s import %s" % \ + (class_name.replace('_getter', ''), class_name) class_def = eval(class_name) return class_def() else: From a58306af45d1fe4356e7be73bcf65d60174c792b Mon Sep 17 00:00:00 2001 From: Fekete Mihai Date: Mon, 10 Nov 2014 16:10:12 +0200 Subject: [PATCH 19/32] Remove trailing whitespaces. --- currency_rate_update/services/currency_getter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/currency_rate_update/services/currency_getter.py b/currency_rate_update/services/currency_getter.py index 495d0477b..e2d24435c 100644 --- a/currency_rate_update/services/currency_getter.py +++ b/currency_rate_update/services/currency_getter.py @@ -74,7 +74,7 @@ class Currency_getter_factory(): ] if class_name in allowed: exec "from update_service_%s import %s" % \ - (class_name.replace('_getter', ''), class_name) + (class_name.replace('_getter', ''), class_name) class_def = eval(class_name) return class_def() else: From 04d13921435e31bff9652669d93bd4d10d439652 Mon Sep 17 00:00:00 2001 From: Fekete Mihai Date: Tue, 11 Nov 2014 16:56:12 +0200 Subject: [PATCH 20/32] Update datetime.time.min method not existent to datetime.min.time(). --- currency_rate_update/model/currency_rate_update.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/currency_rate_update/model/currency_rate_update.py b/currency_rate_update/model/currency_rate_update.py index 500d34ad5..54566bde5 100644 --- a/currency_rate_update/model/currency_rate_update.py +++ b/currency_rate_update/model/currency_rate_update.py @@ -289,7 +289,7 @@ class Currency_rate_update_service(models.Model): if self._context.get('cron', False): next_run = (datetime.combine( fields.Date.from_string(self.next_run), - datetime.time.min) + + datetime.min.time()) + _intervalTypes[str(self.interval_type)] (self.interval_number)).date() self.next_run = next_run From 54afae3d9102ce6e8dcdea8530c911ebd570671f Mon Sep 17 00:00:00 2001 From: Fekete Mihai Date: Thu, 13 Nov 2014 09:34:55 +0200 Subject: [PATCH 21/32] Calculate midnight based on time. --- currency_rate_update/model/currency_rate_update.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/currency_rate_update/model/currency_rate_update.py b/currency_rate_update/model/currency_rate_update.py index 54566bde5..a616e5170 100644 --- a/currency_rate_update/model/currency_rate_update.py +++ b/currency_rate_update/model/currency_rate_update.py @@ -21,7 +21,7 @@ import logging -from datetime import datetime +from datetime import datetime, time from dateutil.relativedelta import relativedelta from openerp import models, fields, api, _ @@ -287,9 +287,10 @@ class Currency_rate_update_service(models.Model): _logger.info(repr(exc)) self.write({'note': error_msg}) if self._context.get('cron', False): + midnight = time(0, 0) next_run = (datetime.combine( fields.Date.from_string(self.next_run), - datetime.min.time()) + + midnight) + _intervalTypes[str(self.interval_type)] (self.interval_number)).date() self.next_run = next_run From 70748150e603f9bd5f5c73fab6063289f8ba1005 Mon Sep 17 00:00:00 2001 From: Fekete Mihai Date: Tue, 18 Nov 2014 08:32:29 +0200 Subject: [PATCH 22/32] Update travis relative imports, warnings. --- currency_rate_update/model/company.py | 2 +- currency_rate_update/model/currency_rate_update.py | 10 +++++----- currency_rate_update/services/currency_getter.py | 2 +- currency_rate_update/services/update_service_CA_BOC.py | 2 +- .../services/update_service_CH_ADMIN.py | 2 +- currency_rate_update/services/update_service_ECB.py | 2 +- currency_rate_update/services/update_service_MX_BdM.py | 2 +- currency_rate_update/services/update_service_PL_NBP.py | 2 +- currency_rate_update/services/update_service_RO_BNR.py | 2 +- currency_rate_update/services/update_service_YAHOO.py | 2 +- 10 files changed, 14 insertions(+), 14 deletions(-) diff --git a/currency_rate_update/model/company.py b/currency_rate_update/model/company.py index e012be7bb..6a9533aec 100644 --- a/currency_rate_update/model/company.py +++ b/currency_rate_update/model/company.py @@ -58,4 +58,4 @@ class res_company(models.Model): services_to_use = fields.One2many( 'currency.rate.update.service', 'company_id', - 'Currency update services') + string='Currency update services') diff --git a/currency_rate_update/model/currency_rate_update.py b/currency_rate_update/model/currency_rate_update.py index a616e5170..03fdffb1c 100644 --- a/currency_rate_update/model/currency_rate_update.py +++ b/currency_rate_update/model/currency_rate_update.py @@ -25,7 +25,7 @@ from datetime import datetime, time from dateutil.relativedelta import relativedelta from openerp import models, fields, api, _ -from openerp.exceptions import Warning +from openerp import exceptions from ..services.currency_getter import Currency_getter_factory @@ -114,13 +114,13 @@ class Currency_rate_update_service(models.Model): @api.constrains('max_delta_days') def _check_max_delta_days(self): if self.max_delta_days < 0: - raise Warning(_('Max delta days must be >= 0')) + raise exceptions.Warning(_('Max delta days must be >= 0')) @api.one @api.constrains('interval_number') def _check_interval_number(self): if self.interval_number < 0: - raise Warning(_('Interval number must be >= 0')) + raise exceptions.Warning(_('Interval number must be >= 0')) @api.onchange('interval_number') def _onchange_interval_number(self): @@ -236,9 +236,9 @@ class Currency_rate_update_service(models.Model): if main_currencies: main_curr = main_currencies[0] else: - raise Warning(_('There is no base currency set!')) + raise exceptions.Warning(_('There is no base currency set!')) if main_curr.rate != 1: - raise Warning(_('Base currency rate should be 1.00!')) + raise exceptions.Warning(_('Base currency rate should be 1.00!')) note = self.note or '' try: # We initalize the class that will handle the request diff --git a/currency_rate_update/services/currency_getter.py b/currency_rate_update/services/currency_getter.py index e2d24435c..833d89d60 100644 --- a/currency_rate_update/services/currency_getter.py +++ b/currency_rate_update/services/currency_getter.py @@ -73,7 +73,7 @@ class Currency_getter_factory(): 'RO_BNR_getter', ] if class_name in allowed: - exec "from update_service_%s import %s" % \ + exec "from .update_service_%s import %s" % \ (class_name.replace('_getter', ''), class_name) class_def = eval(class_name) return class_def() diff --git a/currency_rate_update/services/update_service_CA_BOC.py b/currency_rate_update/services/update_service_CA_BOC.py index 80f53c819..7d7071a4e 100644 --- a/currency_rate_update/services/update_service_CA_BOC.py +++ b/currency_rate_update/services/update_service_CA_BOC.py @@ -20,7 +20,7 @@ # along with this program. If not, see . # ############################################################################## -from currency_getter_interface import Currency_getter_interface +from .currency_getter_interface import Currency_getter_interface from openerp import _ from openerp.exceptions import except_orm diff --git a/currency_rate_update/services/update_service_CH_ADMIN.py b/currency_rate_update/services/update_service_CH_ADMIN.py index 110ad5c5a..1dd1d025a 100644 --- a/currency_rate_update/services/update_service_CH_ADMIN.py +++ b/currency_rate_update/services/update_service_CH_ADMIN.py @@ -20,7 +20,7 @@ # along with this program. If not, see . # ############################################################################## -from currency_getter_interface import Currency_getter_interface +from .currency_getter_interface import Currency_getter_interface import logging _logger = logging.getLogger(__name__) diff --git a/currency_rate_update/services/update_service_ECB.py b/currency_rate_update/services/update_service_ECB.py index ceddce108..d5e815030 100644 --- a/currency_rate_update/services/update_service_ECB.py +++ b/currency_rate_update/services/update_service_ECB.py @@ -20,7 +20,7 @@ # along with this program. If not, see . # ############################################################################## -from currency_getter_interface import Currency_getter_interface +from .currency_getter_interface import Currency_getter_interface from datetime import datetime from openerp.tools import DEFAULT_SERVER_DATE_FORMAT diff --git a/currency_rate_update/services/update_service_MX_BdM.py b/currency_rate_update/services/update_service_MX_BdM.py index 8e3680a18..f923bce38 100644 --- a/currency_rate_update/services/update_service_MX_BdM.py +++ b/currency_rate_update/services/update_service_MX_BdM.py @@ -21,7 +21,7 @@ # ############################################################################## -from currency_getter_interface import Currency_getter_interface +from .currency_getter_interface import Currency_getter_interface import logging _logger = logging.getLogger(__name__) diff --git a/currency_rate_update/services/update_service_PL_NBP.py b/currency_rate_update/services/update_service_PL_NBP.py index 0f57cc265..e354540b4 100644 --- a/currency_rate_update/services/update_service_PL_NBP.py +++ b/currency_rate_update/services/update_service_PL_NBP.py @@ -20,7 +20,7 @@ # along with this program. If not, see . # ############################################################################## -from currency_getter_interface import Currency_getter_interface +from .currency_getter_interface import Currency_getter_interface from datetime import datetime from openerp.tools import DEFAULT_SERVER_DATE_FORMAT diff --git a/currency_rate_update/services/update_service_RO_BNR.py b/currency_rate_update/services/update_service_RO_BNR.py index 2427c4426..c27d2b971 100644 --- a/currency_rate_update/services/update_service_RO_BNR.py +++ b/currency_rate_update/services/update_service_RO_BNR.py @@ -20,7 +20,7 @@ # along with this program. If not, see . # ############################################################################## -from currency_getter_interface import Currency_getter_interface +from .currency_getter_interface import Currency_getter_interface from datetime import datetime, timedelta diff --git a/currency_rate_update/services/update_service_YAHOO.py b/currency_rate_update/services/update_service_YAHOO.py index 7ed18a954..e0b8bdd51 100644 --- a/currency_rate_update/services/update_service_YAHOO.py +++ b/currency_rate_update/services/update_service_YAHOO.py @@ -21,7 +21,7 @@ # ############################################################################## -from currency_getter_interface import Currency_getter_interface +from .currency_getter_interface import Currency_getter_interface class YAHOO_getter(Currency_getter_interface): From d9e0a0151c50de6f8ed2e62fe75f931ebd717781 Mon Sep 17 00:00:00 2001 From: Fekete Mihai Date: Tue, 18 Nov 2014 08:59:57 +0200 Subject: [PATCH 23/32] Fix flake8 errors. --- currency_rate_update/model/currency_rate_update.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/currency_rate_update/model/currency_rate_update.py b/currency_rate_update/model/currency_rate_update.py index 03fdffb1c..88cc83779 100644 --- a/currency_rate_update/model/currency_rate_update.py +++ b/currency_rate_update/model/currency_rate_update.py @@ -238,7 +238,8 @@ class Currency_rate_update_service(models.Model): else: raise exceptions.Warning(_('There is no base currency set!')) if main_curr.rate != 1: - raise exceptions.Warning(_('Base currency rate should be 1.00!')) + raise exceptions.Warning(_('Base currency rate should ' + 'be 1.00!')) note = self.note or '' try: # We initalize the class that will handle the request From b34d2bed48c238a14494c387a202774f086dc930 Mon Sep 17 00:00:00 2001 From: Fekete Mihai Date: Tue, 18 Nov 2014 14:34:08 +0200 Subject: [PATCH 24/32] Update code according to comments. --- .../model/currency_rate_update.py | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/currency_rate_update/model/currency_rate_update.py b/currency_rate_update/model/currency_rate_update.py index 88cc83779..c0e471816 100644 --- a/currency_rate_update/model/currency_rate_update.py +++ b/currency_rate_update/model/currency_rate_update.py @@ -226,18 +226,18 @@ class Currency_rate_update_service(models.Model): # The multi company currency can be set or no so we handle # The two case if company.auto_currency_up: - main_currencies = curr_obj.search( - [('base', '=', True), ('company_id', '=', company.id)]) - if not main_currencies: + main_currency = curr_obj.search( + [('base', '=', True), ('company_id', '=', company.id)], + limit=1) + if not main_currency: # If we can not find a base currency for this company # we look for one with no company set - main_currencies = curr_obj.search( - [('base', '=', True), ('company_id', '=', False)]) - if main_currencies: - main_curr = main_currencies[0] - else: + main_currency = curr_obj.search( + [('base', '=', True), ('company_id', '=', False)], + limit=1) + if not main_currency: raise exceptions.Warning(_('There is no base currency set!')) - if main_curr.rate != 1: + if main_currency.rate != 1: raise exceptions.Warning(_('Base currency rate should ' 'be 1.00!')) note = self.note or '' @@ -249,14 +249,14 @@ class Currency_rate_update_service(models.Model): self.currency_to_update) res, log_info = getter.get_updated_currency( curr_to_fetch, - main_curr.name, + main_currency.name, self.max_delta_days ) rate_name = \ fields.Datetime.to_string(datetime.utcnow().replace( hour=0, minute=0, second=0, microsecond=0)) for curr in self.currency_to_update: - if curr.id == main_curr.id: + if curr.id == main_currency.id: continue do_create = True for rate in curr.rate_ids: @@ -299,12 +299,8 @@ class Currency_rate_update_service(models.Model): @api.multi def run_currency_update(self): "Update currency at the given frequence" - ctx = dict(self._context) - current_date = fields.Date.today() - services = self.search([('next_run', '=', current_date)]) - ctx['cron'] = True - for service in services: - service.with_context(ctx).refresh_currency() + services = self.search([('next_run', '=', fields.Date.today())]) + services.with_context(cron=True).refresh_currency() @api.model def _run_currency_update(self): From 32dab9a350108dcf5245b67ee1a8937292923aac Mon Sep 17 00:00:00 2001 From: Fekete Mihai Date: Tue, 18 Nov 2014 15:03:37 +0200 Subject: [PATCH 25/32] Update CA_BoC messages. --- currency_rate_update/services/update_service_CA_BOC.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/currency_rate_update/services/update_service_CA_BOC.py b/currency_rate_update/services/update_service_CA_BOC.py index 7d7071a4e..6326c15ac 100644 --- a/currency_rate_update/services/update_service_CA_BOC.py +++ b/currency_rate_update/services/update_service_CA_BOC.py @@ -70,8 +70,9 @@ class CA_BOC_getter(Currency_getter_interface): if dom.status != 200: _logger.error("Exchange data for %s is not reported by Bank\ of Canada." % curr) - raise except_orm(_('Error !'), _('Exchange data for %s is not\ - reported by Bank of Canada.' % str(curr))) + raise except_orm(_('Error !'), _('Exchange data for %s is not ' + 'reported by Bank of Canada.' % + str(curr))) _logger.debug("BOC sent a valid RSS file for: " + curr) @@ -91,7 +92,7 @@ class CA_BOC_getter(Currency_getter_interface): "%s. Please check provider data format " "and/or source code." % curr) raise except_orm(_('Error !'), - _('Exchange data format error for\ - Bank of Canada - %s !' % str(curr))) + _('Exchange data format error for ' + 'Bank of Canada - %s !' % str(curr))) return self.updated_currency, self.log_info From 68ef96193f9c74a33a4e98c0d359998a7e34d692 Mon Sep 17 00:00:00 2001 From: Fekete Mihai Date: Tue, 18 Nov 2014 15:33:56 +0200 Subject: [PATCH 26/32] Fix flake8 on CA_BoC service. --- currency_rate_update/services/update_service_CA_BOC.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/currency_rate_update/services/update_service_CA_BOC.py b/currency_rate_update/services/update_service_CA_BOC.py index 6326c15ac..5cd0e3e49 100644 --- a/currency_rate_update/services/update_service_CA_BOC.py +++ b/currency_rate_update/services/update_service_CA_BOC.py @@ -71,8 +71,8 @@ class CA_BOC_getter(Currency_getter_interface): _logger.error("Exchange data for %s is not reported by Bank\ of Canada." % curr) raise except_orm(_('Error !'), _('Exchange data for %s is not ' - 'reported by Bank of Canada.' % - str(curr))) + 'reported by Bank of Canada.' + % str(curr))) _logger.debug("BOC sent a valid RSS file for: " + curr) From 95e865f29ea6c0661ee7376f677af1e0cafb21a5 Mon Sep 17 00:00:00 2001 From: Fekete Mihai Date: Wed, 19 Nov 2014 11:30:10 +0200 Subject: [PATCH 27/32] Update strings. --- currency_rate_update/i18n/ab.po | 180 ---------------- .../i18n/currency_rate_update.pot | 173 ---------------- currency_rate_update/i18n/es.po | 184 ----------------- currency_rate_update/i18n/fr_FR.po | 181 ----------------- currency_rate_update/i18n/pl.po | 180 ---------------- currency_rate_update/i18n/pl_PL.po | 192 ------------------ currency_rate_update/i18n/pt_BR.po | 179 ---------------- currency_rate_update/model/company.py | 4 +- .../model/currency_rate_update.py | 30 +-- currency_rate_update/view/company_view.xml | 4 +- .../view/currency_rate_update.xml | 4 +- 11 files changed, 21 insertions(+), 1290 deletions(-) delete mode 100644 currency_rate_update/i18n/ab.po delete mode 100644 currency_rate_update/i18n/currency_rate_update.pot delete mode 100644 currency_rate_update/i18n/es.po delete mode 100644 currency_rate_update/i18n/fr_FR.po delete mode 100644 currency_rate_update/i18n/pl.po delete mode 100644 currency_rate_update/i18n/pl_PL.po delete mode 100644 currency_rate_update/i18n/pt_BR.po diff --git a/currency_rate_update/i18n/ab.po b/currency_rate_update/i18n/ab.po deleted file mode 100644 index 9206a22e5..000000000 --- a/currency_rate_update/i18n/ab.po +++ /dev/null @@ -1,180 +0,0 @@ -# Translation of OpenERP Server. -# This file contains the translation of the following modules: -# * c2c_currency_rate_update -# -msgid "" -msgstr "" -"Project-Id-Version: OpenERP Server 5.0.0\n" -"Report-Msgid-Bugs-To: support@openerp.com\n" -"POT-Creation-Date: 2013-10-18 17:52+0000\n" -"PO-Revision-Date: 2013-11-11 16:24+0000\n" -"Last-Translator: Grzegorz Grzelak (Cirrus) \n" -"Language-Team: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-06-12 06:31+0000\n" -"X-Generator: Launchpad (build 17041)\n" - -#. module: currency_rate_update -#: view:res.company:0 -msgid "Currency auto update configuration" -msgstr "Aktualizacja kursów walut" - -#. module: currency_rate_update -#: view:res.company:0 -msgid "Currency updates services" -msgstr "Serwisy aktualizacji kursów" - -#. module: currency_rate_update -#: field:currency.rate.update.service,company_id:0 -msgid "linked company" -msgstr "dla firmy" - -#. module: currency_rate_update -#: view:currency.rate.update.service:0 -msgid "Rate" -msgstr "Kurs" - -#. module: currency_rate_update -#: view:currency.rate.update.service:0 -msgid "Rates" -msgstr "Kursy" - -#. module: currency_rate_update -#: field:res.company,multi_company_currency_enable:0 -msgid "Multi company currency" -msgstr "Waluta dla wielu firm" - -#. module: currency_rate_update -#: field:res.company,interval_type:0 -msgid "Currency update frequence" -msgstr "Częstotliwość aktualizacji kursów" - -#. module: currency_rate_update -#: selection:res.company,interval_type:0 -msgid "Day(s)" -msgstr "Codziennie" - -#. module: currency_rate_update -#: field:currency.rate.update.service,currency_to_update:0 -msgid "currency to update with this service" -msgstr "Waluty aktualizowane tym serwisem" - -#. module: currency_rate_update -#: selection:currency.rate.update.service,service:0 -msgid "Narodowy Bank Polski" -msgstr "" - -#. module: currency_rate_update -#: field:currency.rate.update.service,note:0 -msgid "update notice" -msgstr "Notatka aktualizacji" - -#. module: currency_rate_update -#: field:currency.rate.update.service,service:0 -msgid "Webservice to use" -msgstr "Strona web do zastosowania" - -#. module: currency_rate_update -#: help:currency.rate.update.service,max_delta_days:0 -msgid "" -"If the time delta between the rate date given by the webservice and the " -"current date exeeds this value, then the currency rate is not updated in " -"OpenERP." -msgstr "" - -#. module: currency_rate_update -#: field:res.company,services_to_use:0 -msgid "Currency update services" -msgstr "Serwisy aktualizacji kursów" - -#. module: currency_rate_update -#: constraint:currency.rate.update.service:0 -msgid "'Max delta days' must be >= 0" -msgstr "" - -#. module: currency_rate_update -#: code:addons/currency_rate_update/currency_rate_update.py:89 -#: sql_constraint:currency.rate.update.service:0 -#, python-format -msgid "You can use a service one time per company !" -msgstr "Możesz stosować jeden serwis na firmę !" - -#. module: currency_rate_update -#: selection:currency.rate.update.service,service:0 -msgid "Yahoo Finance " -msgstr "" - -#. module: currency_rate_update -#: field:currency.rate.update.service,max_delta_days:0 -msgid "Max delta days" -msgstr "" - -#. module: currency_rate_update -#: selection:currency.rate.update.service,service:0 -msgid "Admin.ch" -msgstr "" - -#. module: currency_rate_update -#: model:ir.model,name:currency_rate_update.model_res_company -msgid "Companies" -msgstr "" - -#. module: currency_rate_update -#: view:currency.rate.update.service:0 -msgid "Currencies to update with this service" -msgstr "" - -#. module: currency_rate_update -#: selection:res.company,interval_type:0 -msgid "Week(s)" -msgstr "Co tydzień" - -#. module: currency_rate_update -#: help:res.company,multi_company_currency_enable:0 -msgid "" -"if this case is not check you can not set currency is active on two company" -msgstr "" -"Jeśli nie zaznaczysz, to nie będziesz mógł ustawić waluty jako aktywnej dla " -"dwóch firm" - -#. module: currency_rate_update -#: code:addons/currency_rate_update/currency_rate_update.py:153 -#: model:ir.model,name:currency_rate_update.model_currency_rate_update -#: model:ir.model,name:currency_rate_update.model_currency_rate_update_service -#, python-format -msgid "Currency Rate Update" -msgstr "Aktualizacja kursów walut" - -#. module: currency_rate_update -#: view:currency.rate.update.service:0 -msgid "Logs" -msgstr "" - -#. module: currency_rate_update -#: field:res.company,auto_currency_up:0 -msgid "Automatical update of the currency this company" -msgstr "Automatyczna aktualizacja kursów walut dla tej firmy" - -#. module: currency_rate_update -#: selection:res.company,interval_type:0 -msgid "Month(s)" -msgstr "Co miesiąc" - -#. module: currency_rate_update -#: selection:currency.rate.update.service,service:0 -msgid "European Central Bank" -msgstr "" - -#. module: currency_rate_update -#: help:res.company,interval_type:0 -msgid "" -"changing this value will\n" -" also affect other compagnies" -msgstr "Zmiana tej wartości wpłynie również na inne firmy" - -#. module: currency_rate_update -#: view:res.company:0 -msgid "Refresh currencies" -msgstr "Aktualizuj kursy" diff --git a/currency_rate_update/i18n/currency_rate_update.pot b/currency_rate_update/i18n/currency_rate_update.pot deleted file mode 100644 index 4f0548074..000000000 --- a/currency_rate_update/i18n/currency_rate_update.pot +++ /dev/null @@ -1,173 +0,0 @@ -# Translation of OpenERP Server. -# This file contains the translation of the following modules: -# * currency_rate_update -# -msgid "" -msgstr "" -"Project-Id-Version: OpenERP Server 7.0\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-10-18 17:52+0000\n" -"PO-Revision-Date: 2013-10-18 17:52+0000\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 -#: view:res.company:0 -msgid "Currency auto update configuration" -msgstr "" - -#. module: currency_rate_update -#: view:res.company:0 -msgid "Currency updates services" -msgstr "" - -#. module: currency_rate_update -#: field:currency.rate.update.service,company_id:0 -msgid "linked company" -msgstr "" - -#. module: currency_rate_update -#: view:currency.rate.update.service:0 -msgid "Rate" -msgstr "" - -#. module: currency_rate_update -#: view:currency.rate.update.service:0 -msgid "Rates" -msgstr "" - -#. module: currency_rate_update -#: field:res.company,multi_company_currency_enable:0 -msgid "Multi company currency" -msgstr "" - -#. module: currency_rate_update -#: field:res.company,interval_type:0 -msgid "Currency update frequence" -msgstr "" - -#. module: currency_rate_update -#: selection:res.company,interval_type:0 -msgid "Day(s)" -msgstr "" - -#. module: currency_rate_update -#: field:currency.rate.update.service,currency_to_update:0 -msgid "currency to update with this service" -msgstr "" - -#. module: currency_rate_update -#: selection:currency.rate.update.service,service:0 -msgid "Narodowy Bank Polski" -msgstr "" - -#. module: currency_rate_update -#: field:currency.rate.update.service,note:0 -msgid "update notice" -msgstr "" - -#. module: currency_rate_update -#: field:currency.rate.update.service,service:0 -msgid "Webservice to use" -msgstr "" - -#. module: currency_rate_update -#: help:currency.rate.update.service,max_delta_days:0 -msgid "If the time delta between the rate date given by the webservice and the current date exeeds this value, then the currency rate is not updated in OpenERP." -msgstr "" - -#. module: currency_rate_update -#: field:res.company,services_to_use:0 -msgid "Currency update services" -msgstr "" - -#. module: currency_rate_update -#: constraint:currency.rate.update.service:0 -msgid "'Max delta days' must be >= 0" -msgstr "" - -#. module: currency_rate_update -#: code:addons/currency_rate_update/currency_rate_update.py:89 -#: sql_constraint:currency.rate.update.service:0 -#, python-format -msgid "You can use a service one time per company !" -msgstr "" - -#. module: currency_rate_update -#: selection:currency.rate.update.service,service:0 -msgid "Yahoo Finance " -msgstr "" - -#. module: currency_rate_update -#: field:currency.rate.update.service,max_delta_days:0 -msgid "Max delta days" -msgstr "" - -#. module: currency_rate_update -#: selection:currency.rate.update.service,service:0 -msgid "Admin.ch" -msgstr "" - -#. module: currency_rate_update -#: model:ir.model,name:currency_rate_update.model_res_company -msgid "Companies" -msgstr "" - -#. module: currency_rate_update -#: view:currency.rate.update.service:0 -msgid "Currencies to update with this service" -msgstr "" - -#. module: currency_rate_update -#: selection:res.company,interval_type:0 -msgid "Week(s)" -msgstr "" - -#. module: currency_rate_update -#: help:res.company,multi_company_currency_enable:0 -msgid "if this case is not check you can not set currency is active on two company" -msgstr "" - -#. module: currency_rate_update -#: code:addons/currency_rate_update/currency_rate_update.py:153 -#: model:ir.model,name:currency_rate_update.model_currency_rate_update -#: model:ir.model,name:currency_rate_update.model_currency_rate_update_service -#, python-format -msgid "Currency Rate Update" -msgstr "" - -#. module: currency_rate_update -#: view:currency.rate.update.service:0 -msgid "Logs" -msgstr "" - -#. module: currency_rate_update -#: field:res.company,auto_currency_up:0 -msgid "Automatical update of the currency this company" -msgstr "" - -#. module: currency_rate_update -#: selection:res.company,interval_type:0 -msgid "Month(s)" -msgstr "" - -#. module: currency_rate_update -#: selection:currency.rate.update.service,service:0 -msgid "European Central Bank" -msgstr "" - -#. module: currency_rate_update -#: help:res.company,interval_type:0 -msgid "changing this value will\n" -" also affect other compagnies" -msgstr "" - -#. module: currency_rate_update -#: view:res.company:0 -msgid "Refresh currencies" -msgstr "" - diff --git a/currency_rate_update/i18n/es.po b/currency_rate_update/i18n/es.po deleted file mode 100644 index 7e01c95f8..000000000 --- a/currency_rate_update/i18n/es.po +++ /dev/null @@ -1,184 +0,0 @@ -# Spanish translation for account-financial-tools -# Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 -# This file is distributed under the same license as the account-financial-tools package. -# FIRST AUTHOR , 2014. -# -msgid "" -msgstr "" -"Project-Id-Version: account-financial-tools\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2013-10-18 17:52+0000\n" -"PO-Revision-Date: 2014-04-08 19:54+0000\n" -"Last-Translator: Pedro Manuel Baeza \n" -"Language-Team: Spanish \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-06-12 06:31+0000\n" -"X-Generator: Launchpad (build 17041)\n" - -#. module: currency_rate_update -#: view:res.company:0 -msgid "Currency auto update configuration" -msgstr "Configuración de auto actualización de las tasas de cambio" - -#. module: currency_rate_update -#: view:res.company:0 -msgid "Currency updates services" -msgstr "Servicios de actualización de las tasas de cambio" - -#. module: currency_rate_update -#: field:currency.rate.update.service,company_id:0 -msgid "linked company" -msgstr "compañía relacionada" - -#. module: currency_rate_update -#: view:currency.rate.update.service:0 -msgid "Rate" -msgstr "Tasa" - -#. module: currency_rate_update -#: view:currency.rate.update.service:0 -msgid "Rates" -msgstr "Tasas" - -#. module: currency_rate_update -#: field:res.company,multi_company_currency_enable:0 -msgid "Multi company currency" -msgstr "Moneda multi-compañía" - -#. module: currency_rate_update -#: field:res.company,interval_type:0 -msgid "Currency update frequence" -msgstr "Frecuencia de actualización" - -#. module: currency_rate_update -#: selection:res.company,interval_type:0 -msgid "Day(s)" -msgstr "Día(s)" - -#. module: currency_rate_update -#: field:currency.rate.update.service,currency_to_update:0 -msgid "currency to update with this service" -msgstr "moneda a actualizar con este servicio" - -#. module: currency_rate_update -#: selection:currency.rate.update.service,service:0 -msgid "Narodowy Bank Polski" -msgstr "Narodowy Bank Polski" - -#. module: currency_rate_update -#: field:currency.rate.update.service,note:0 -msgid "update notice" -msgstr "nota de actualización" - -#. module: currency_rate_update -#: field:currency.rate.update.service,service:0 -msgid "Webservice to use" -msgstr "Servicio web a utilizar" - -#. module: currency_rate_update -#: help:currency.rate.update.service,max_delta_days:0 -msgid "" -"If the time delta between the rate date given by the webservice and the " -"current date exeeds this value, then the currency rate is not updated in " -"OpenERP." -msgstr "" -"Si la diferencia de tiempo entre la fecha de la tasa dada por el servicio " -"web y la fecha actual excede este valor, entonces la tasa de cambio de " -"divisa no se actualizará en OpenERP." - -#. module: currency_rate_update -#: field:res.company,services_to_use:0 -msgid "Currency update services" -msgstr "Servicios de actualización de la tasa de cambio" - -#. module: currency_rate_update -#: constraint:currency.rate.update.service:0 -msgid "'Max delta days' must be >= 0" -msgstr "Los días de diferencia máximos deben ser mayor o igual que 0" - -#. module: currency_rate_update -#: code:addons/currency_rate_update/currency_rate_update.py:89 -#: sql_constraint:currency.rate.update.service:0 -#, python-format -msgid "You can use a service one time per company !" -msgstr "Sólo puedes usar una vez un servicio por compañía" - -#. module: currency_rate_update -#: selection:currency.rate.update.service,service:0 -msgid "Yahoo Finance " -msgstr "Yahoo Finance " - -#. module: currency_rate_update -#: field:currency.rate.update.service,max_delta_days:0 -msgid "Max delta days" -msgstr "Días de diferencia máximos" - -#. module: currency_rate_update -#: selection:currency.rate.update.service,service:0 -msgid "Admin.ch" -msgstr "Admin.ch" - -#. module: currency_rate_update -#: model:ir.model,name:currency_rate_update.model_res_company -msgid "Companies" -msgstr "Compañías" - -#. module: currency_rate_update -#: view:currency.rate.update.service:0 -msgid "Currencies to update with this service" -msgstr "Monedas a actualizar con este servicio" - -#. module: currency_rate_update -#: selection:res.company,interval_type:0 -msgid "Week(s)" -msgstr "Semana(s)" - -#. module: currency_rate_update -#: help:res.company,multi_company_currency_enable:0 -msgid "" -"if this case is not check you can not set currency is active on two company" -msgstr "" -"Si está casilla no está marcada, no podrá marcar que una moneda está activa " -"en dos compañías" - -#. module: currency_rate_update -#: code:addons/currency_rate_update/currency_rate_update.py:153 -#: model:ir.model,name:currency_rate_update.model_currency_rate_update -#: model:ir.model,name:currency_rate_update.model_currency_rate_update_service -#, python-format -msgid "Currency Rate Update" -msgstr "Actualización del tipo de cambio" - -#. module: currency_rate_update -#: view:currency.rate.update.service:0 -msgid "Logs" -msgstr "Registro" - -#. module: currency_rate_update -#: field:res.company,auto_currency_up:0 -msgid "Automatical update of the currency this company" -msgstr "Actualización automática del tipo de cambio de esta compañía" - -#. module: currency_rate_update -#: selection:res.company,interval_type:0 -msgid "Month(s)" -msgstr "Mes(es)" - -#. module: currency_rate_update -#: selection:currency.rate.update.service,service:0 -msgid "European Central Bank" -msgstr "Banco Central Europeo" - -#. module: currency_rate_update -#: help:res.company,interval_type:0 -msgid "" -"changing this value will\n" -" also affect other compagnies" -msgstr "cambiando este valor, también afectará a otras compañías" - -#. module: currency_rate_update -#: view:res.company:0 -msgid "Refresh currencies" -msgstr "Actualizar tipos de cambio" diff --git a/currency_rate_update/i18n/fr_FR.po b/currency_rate_update/i18n/fr_FR.po deleted file mode 100644 index ff5d54088..000000000 --- a/currency_rate_update/i18n/fr_FR.po +++ /dev/null @@ -1,181 +0,0 @@ -# Translation of OpenERP Server. -# This file contains the translation of the following modules: -# * c2c_currency_rate_update -# -msgid "" -msgstr "" -"Project-Id-Version: OpenERP Server 5.0.0\n" -"Report-Msgid-Bugs-To: support@openerp.com\n" -"POT-Creation-Date: 2013-10-18 17:52+0000\n" -"PO-Revision-Date: 2013-11-11 16:24+0000\n" -"Last-Translator: Alexandre Fayolle - camptocamp " -"\n" -"Language-Team: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-06-12 06:31+0000\n" -"X-Generator: Launchpad (build 17041)\n" - -#. module: currency_rate_update -#: view:res.company:0 -msgid "Currency auto update configuration" -msgstr "Configuration de la mise à jour automatique des taux de change" - -#. module: currency_rate_update -#: view:res.company:0 -msgid "Currency updates services" -msgstr "Services de mise à jour des taux de change" - -#. module: currency_rate_update -#: field:currency.rate.update.service,company_id:0 -msgid "linked company" -msgstr "Société liée" - -#. module: currency_rate_update -#: view:currency.rate.update.service:0 -msgid "Rate" -msgstr "Taux de change" - -#. module: currency_rate_update -#: view:currency.rate.update.service:0 -msgid "Rates" -msgstr "Taux de change" - -#. module: currency_rate_update -#: field:res.company,multi_company_currency_enable:0 -msgid "Multi company currency" -msgstr "Devies mutli société" - -#. module: currency_rate_update -#: field:res.company,interval_type:0 -msgid "Currency update frequence" -msgstr "Fréquence de mise à jour" - -#. module: currency_rate_update -#: selection:res.company,interval_type:0 -msgid "Day(s)" -msgstr "Quotidien" - -#. module: currency_rate_update -#: field:currency.rate.update.service,currency_to_update:0 -msgid "currency to update with this service" -msgstr "Taux de change à mettre à jour avec ce service" - -#. module: currency_rate_update -#: selection:currency.rate.update.service,service:0 -msgid "Narodowy Bank Polski" -msgstr "" - -#. module: currency_rate_update -#: field:currency.rate.update.service,note:0 -msgid "update notice" -msgstr "Notice de mise à jour" - -#. module: currency_rate_update -#: field:currency.rate.update.service,service:0 -msgid "Webservice to use" -msgstr "Service web à utliser" - -#. module: currency_rate_update -#: help:currency.rate.update.service,max_delta_days:0 -msgid "" -"If the time delta between the rate date given by the webservice and the " -"current date exeeds this value, then the currency rate is not updated in " -"OpenERP." -msgstr "" - -#. module: currency_rate_update -#: field:res.company,services_to_use:0 -msgid "Currency update services" -msgstr "Service de mise à jour des taux de change" - -#. module: currency_rate_update -#: constraint:currency.rate.update.service:0 -msgid "'Max delta days' must be >= 0" -msgstr "" - -#. module: currency_rate_update -#: code:addons/currency_rate_update/currency_rate_update.py:89 -#: sql_constraint:currency.rate.update.service:0 -#, python-format -msgid "You can use a service one time per company !" -msgstr "Vous ne pouvez utiliser un service qu'une fois par société" - -#. module: currency_rate_update -#: selection:currency.rate.update.service,service:0 -msgid "Yahoo Finance " -msgstr "" - -#. module: currency_rate_update -#: field:currency.rate.update.service,max_delta_days:0 -msgid "Max delta days" -msgstr "" - -#. module: currency_rate_update -#: selection:currency.rate.update.service,service:0 -msgid "Admin.ch" -msgstr "" - -#. module: currency_rate_update -#: model:ir.model,name:currency_rate_update.model_res_company -msgid "Companies" -msgstr "" - -#. module: currency_rate_update -#: view:currency.rate.update.service:0 -msgid "Currencies to update with this service" -msgstr "" - -#. module: currency_rate_update -#: selection:res.company,interval_type:0 -msgid "Week(s)" -msgstr "Hebdomadaire" - -#. module: currency_rate_update -#: help:res.company,multi_company_currency_enable:0 -msgid "" -"if this case is not check you can not set currency is active on two company" -msgstr "" -"Si cette case n'est pas cochée vous ne pouvez pas utiliser la mise à jour " -"sur plusieurs sociétés" - -#. module: currency_rate_update -#: code:addons/currency_rate_update/currency_rate_update.py:153 -#: model:ir.model,name:currency_rate_update.model_currency_rate_update -#: model:ir.model,name:currency_rate_update.model_currency_rate_update_service -#, python-format -msgid "Currency Rate Update" -msgstr "Mise à jour des taux de change" - -#. module: currency_rate_update -#: view:currency.rate.update.service:0 -msgid "Logs" -msgstr "" - -#. module: currency_rate_update -#: field:res.company,auto_currency_up:0 -msgid "Automatical update of the currency this company" -msgstr "Mise à jour automatique des taux de change" - -#. module: currency_rate_update -#: selection:res.company,interval_type:0 -msgid "Month(s)" -msgstr "Mensuelle" - -#. module: currency_rate_update -#: selection:currency.rate.update.service,service:0 -msgid "European Central Bank" -msgstr "" - -#. module: currency_rate_update -#: help:res.company,interval_type:0 -msgid "" -"changing this value will\n" -" also affect other compagnies" -msgstr "Modifier cette devise va aussi affecter les autre sociétés" - -#. module: currency_rate_update -#: view:res.company:0 -msgid "Refresh currencies" -msgstr "Mettre à jour les taux de change" diff --git a/currency_rate_update/i18n/pl.po b/currency_rate_update/i18n/pl.po deleted file mode 100644 index 4c7777944..000000000 --- a/currency_rate_update/i18n/pl.po +++ /dev/null @@ -1,180 +0,0 @@ -# Translation of OpenERP Server. -# This file contains the translation of the following modules: -# * c2c_currency_rate_update -# -msgid "" -msgstr "" -"Project-Id-Version: OpenERP Server 5.0.0\n" -"Report-Msgid-Bugs-To: support@openerp.com\n" -"POT-Creation-Date: 2013-10-18 17:52+0000\n" -"PO-Revision-Date: 2013-11-11 16:33+0000\n" -"Last-Translator: Grzegorz Grzelak (Cirrus) \n" -"Language-Team: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-06-12 06:31+0000\n" -"X-Generator: Launchpad (build 17041)\n" - -#. module: currency_rate_update -#: view:res.company:0 -msgid "Currency auto update configuration" -msgstr "Aktualizacja kursów walut" - -#. module: currency_rate_update -#: view:res.company:0 -msgid "Currency updates services" -msgstr "Serwisy aktualizacji kursów" - -#. module: currency_rate_update -#: field:currency.rate.update.service,company_id:0 -msgid "linked company" -msgstr "dla firmy" - -#. module: currency_rate_update -#: view:currency.rate.update.service:0 -msgid "Rate" -msgstr "Kurs" - -#. module: currency_rate_update -#: view:currency.rate.update.service:0 -msgid "Rates" -msgstr "Kursy" - -#. module: currency_rate_update -#: field:res.company,multi_company_currency_enable:0 -msgid "Multi company currency" -msgstr "Waluta dla wielu firm" - -#. module: currency_rate_update -#: field:res.company,interval_type:0 -msgid "Currency update frequence" -msgstr "Częstotliwość aktualizacji kursów" - -#. module: currency_rate_update -#: selection:res.company,interval_type:0 -msgid "Day(s)" -msgstr "Codziennie" - -#. module: currency_rate_update -#: field:currency.rate.update.service,currency_to_update:0 -msgid "currency to update with this service" -msgstr "Waluty aktualizowane tym serwisem" - -#. module: currency_rate_update -#: selection:currency.rate.update.service,service:0 -msgid "Narodowy Bank Polski" -msgstr "" - -#. module: currency_rate_update -#: field:currency.rate.update.service,note:0 -msgid "update notice" -msgstr "Notatka aktualizacji" - -#. module: currency_rate_update -#: field:currency.rate.update.service,service:0 -msgid "Webservice to use" -msgstr "Strona web do zastosowania" - -#. module: currency_rate_update -#: help:currency.rate.update.service,max_delta_days:0 -msgid "" -"If the time delta between the rate date given by the webservice and the " -"current date exeeds this value, then the currency rate is not updated in " -"OpenERP." -msgstr "" - -#. module: currency_rate_update -#: field:res.company,services_to_use:0 -msgid "Currency update services" -msgstr "Serwisy aktualizacji kursów" - -#. module: currency_rate_update -#: constraint:currency.rate.update.service:0 -msgid "'Max delta days' must be >= 0" -msgstr "" - -#. module: currency_rate_update -#: code:addons/currency_rate_update/currency_rate_update.py:89 -#: sql_constraint:currency.rate.update.service:0 -#, python-format -msgid "You can use a service one time per company !" -msgstr "Możesz stosować jeden serwis na firmę !" - -#. module: currency_rate_update -#: selection:currency.rate.update.service,service:0 -msgid "Yahoo Finance " -msgstr "" - -#. module: currency_rate_update -#: field:currency.rate.update.service,max_delta_days:0 -msgid "Max delta days" -msgstr "" - -#. module: currency_rate_update -#: selection:currency.rate.update.service,service:0 -msgid "Admin.ch" -msgstr "" - -#. module: currency_rate_update -#: model:ir.model,name:currency_rate_update.model_res_company -msgid "Companies" -msgstr "" - -#. module: currency_rate_update -#: view:currency.rate.update.service:0 -msgid "Currencies to update with this service" -msgstr "" - -#. module: currency_rate_update -#: selection:res.company,interval_type:0 -msgid "Week(s)" -msgstr "Co tydzień" - -#. module: currency_rate_update -#: help:res.company,multi_company_currency_enable:0 -msgid "" -"if this case is not check you can not set currency is active on two company" -msgstr "" -"Jeśli nie zaznaczysz, to nie będziesz mógł ustawić waluty jako aktywnej dla " -"dwóch firm" - -#. module: currency_rate_update -#: code:addons/currency_rate_update/currency_rate_update.py:153 -#: model:ir.model,name:currency_rate_update.model_currency_rate_update -#: model:ir.model,name:currency_rate_update.model_currency_rate_update_service -#, python-format -msgid "Currency Rate Update" -msgstr "Aktualizacja kursów walut" - -#. module: currency_rate_update -#: view:currency.rate.update.service:0 -msgid "Logs" -msgstr "" - -#. module: currency_rate_update -#: field:res.company,auto_currency_up:0 -msgid "Automatical update of the currency this company" -msgstr "Automatyczna aktualizacja kursów walut dla tej firmy" - -#. module: currency_rate_update -#: selection:res.company,interval_type:0 -msgid "Month(s)" -msgstr "Co miesiąc" - -#. module: currency_rate_update -#: selection:currency.rate.update.service,service:0 -msgid "European Central Bank" -msgstr "" - -#. module: currency_rate_update -#: help:res.company,interval_type:0 -msgid "" -"changing this value will\n" -" also affect other compagnies" -msgstr "Zmiana tej wartości wpłynie również na inne firmy" - -#. module: currency_rate_update -#: view:res.company:0 -msgid "Refresh currencies" -msgstr "Aktualizuj kursy" diff --git a/currency_rate_update/i18n/pl_PL.po b/currency_rate_update/i18n/pl_PL.po deleted file mode 100644 index 2cba33598..000000000 --- a/currency_rate_update/i18n/pl_PL.po +++ /dev/null @@ -1,192 +0,0 @@ -# Translation of OpenERP Server. -# This file contains the translation of the following modules: -# * c2c_currency_rate_update -# -msgid "" -msgstr "" -"Project-Id-Version: OpenERP Server 5.0.0\n" -"Report-Msgid-Bugs-To: support@openerp.com\n" -"POT-Creation-Date: 2009-06-17 08:25:57+0000\n" -"PO-Revision-Date: 2009-11-12 11:39+0100\n" -"Last-Translator: Grzegorz Grzelak (Cirrus) \n" -"Language-Team: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: \n" - -#. module: c2c_currency_rate_update -#: view:res.company:0 -msgid "Currency auto update configuration" -msgstr "Aktualizacja kursów walut" - -#. module: c2c_currency_rate_update -#: view:res.company:0 -msgid "Currency updates services" -msgstr "Serwisy aktualizacji kursów" - -#. module: c2c_currency_rate_update -#: field:currency.rate.update.service,company_id:0 -msgid "linked company" -msgstr "dla firmy" - -#. module: c2c_currency_rate_update -#: view:currency.rate.update.service:0 -msgid "Rates" -msgstr "Kursy" - -#. module: c2c_currency_rate_update -#: constraint:ir.model:0 -msgid "The Object name must start with x_ and not contain any special character !" -msgstr "Nazwa obiektu musi zaczynać się od x_ oraz nie może zawierać znaków specjalnych !" - -#. module: c2c_currency_rate_update -#: field:res.company,multi_company_currency_enable:0 -msgid "Multi company currency" -msgstr "Waluta dla wielu firm" - -#. module: c2c_currency_rate_update -#: field:res.company,interval_type:0 -msgid "Currency update frequence" -msgstr "Częstotliwość aktualizacji kursów" - -#. module: c2c_currency_rate_update -#: selection:res.company,interval_type:0 -msgid "Day(s)" -msgstr "Codziennie" - -#. module: c2c_currency_rate_update -#: field:currency.rate.update.service,currency_to_update:0 -msgid "currency to update with this service" -msgstr "Waluty aktualizowane tym serwisem" - -#. module: c2c_currency_rate_update -#: field:currency.rate.update.service,note:0 -msgid "update notice" -msgstr "Notatka aktualizacji" - -#. module: c2c_currency_rate_update -#: field:currency.rate.update.service,service:0 -msgid "Webservice to use" -msgstr "Strona web do zastosowania" - -#. module: c2c_currency_rate_update -#: selection:res.company,interval_type:0 -msgid "Month(s)" -msgstr "Co miesiąc" - -#. module: c2c_currency_rate_update -#: view:currency.rate.update.service:0 -msgid "note" -msgstr "Notatka" - -#. module: c2c_currency_rate_update -#: field:res.company,services_to_use:0 -msgid "Currency update services" -msgstr "Serwisy aktualizacji kursów" - -#. module: c2c_currency_rate_update -#: view:currency.rate.update.service:0 -msgid "Currencies to update with this services" -msgstr "Waluty aktualizowane tymi serwisami" - -#. module: c2c_currency_rate_update -#: code:addons/c2c_currency_rate_update/currency_rate_update.py:0 -#, python-format -msgid "You can use a service one time per company !" -msgstr "Możesz stosować jeden serwis na firmę !" - -#. module: c2c_currency_rate_update -#: selection:currency.rate.update.service,service:0 -msgid "Yahoo Finance " -msgstr "" - -#. module: c2c_currency_rate_update -#: constraint:ir.ui.view:0 -msgid "Invalid XML for View Architecture!" -msgstr "XML niewłaściwy dla tej architektury wyświetlania!" - -#. module: c2c_currency_rate_update -#: selection:currency.rate.update.service,service:0 -msgid "Admin.ch" -msgstr "" - -#. module: c2c_currency_rate_update -#: selection:res.company,interval_type:0 -msgid "Week(s)" -msgstr "Co tydzień" - -#. module: c2c_currency_rate_update -#: help:res.company,multi_company_currency_enable:0 -msgid "if this case is not check you can not set currency is active on two company" -msgstr "Jeśli nie zaznaczysz, to nie będziesz mógł ustawić waluty jako aktywnej dla dwóch firm" - -#. module: c2c_currency_rate_update -#: code:addons/c2c_currency_rate_update/currency_rate_update.py:0 -#: model:ir.model,name:c2c_currency_rate_update.model_currency_rate_update -#: model:ir.model,name:c2c_currency_rate_update.model_currency_rate_update_service -#: model:ir.module.module,shortdesc:c2c_currency_rate_update.module_name_translation -#, python-format -msgid "Currency Rate Update" -msgstr "Aktualizacja kursów walut" - -#. module: c2c_currency_rate_update -#: model:ir.module.module,description:c2c_currency_rate_update.module_name_translation -msgid "" -"\n" -"Import exchange rates from three different sources on the internet :\n" -"\n" -"1. Admin.ch\n" -" Updated daily, source in CHF. Source type is XML based.\n" -"\n" -"2. Federal Reserve Bank of New York\n" -" Daily 12 noon buying rates in New York are certified by the\n" -" New York Federal Reserve Bank for customs purposes.\n" -" Source in USD.\n" -" http://www.newyorkfed.org/markets/pilotfx.html\n" -"\n" -"3. European Central Bank\n" -" The reference rates are based on the regular daily concertation procedure between\n" -" central banks within and outside the European System of Central Banks,\n" -" which normally takes place at 2.15 p.m. (14:15) ECB time. Source in EUR.\n" -" http://www.ecb.europa.eu/stats/exchange/eurofxref/html/index.en.html\n" -"\n" -"4. Google Finance\n" -" Updated daily from Citibank N.A., source in EUR. Information may be delayed.\n" -" This is parsed from an HTML page, so it may be broken at anytime.\n" -"\n" -"5. Bank of Canada\n" -" Updated daily at 12:30 am, source in CAD, nominal rate. Source type is CSV based.\n" -"\n" -"You can set time cycle for getting updates, 'first execute date' define when to start\n" -"the import and you can add a comment that describe why you use that particular service.\n" -"\n" -"The module uses internal ir_cron feature from OpenERP, so the job is launched once\n" -"the server starts if the 'first execute date' is before the current day.\n" -"\n" -"If in multi-company mode, the base currency will be the first company's currency\n" -"found in database.\n" -msgstr "" - -#. module: c2c_currency_rate_update -#: field:res.company,auto_currency_up:0 -msgid "Automatical update of the currency this company" -msgstr "Automatyczna aktualizacja kursów walut dla tej firmy" - -#. module: c2c_currency_rate_update -#: view:currency.rate.update.service:0 -msgid "Rate" -msgstr "Kurs" - -#. module: c2c_currency_rate_update -#: help:res.company,interval_type:0 -msgid "" -"changing this value will\n" -" also affect other compagnies" -msgstr "Zmiana tej wartości wpłynie również na inne firmy" - -#. module: c2c_currency_rate_update -#: view:res.company:0 -msgid "Refresh currencies" -msgstr "Aktualizuj kursy" - diff --git a/currency_rate_update/i18n/pt_BR.po b/currency_rate_update/i18n/pt_BR.po deleted file mode 100644 index 79b78bc64..000000000 --- a/currency_rate_update/i18n/pt_BR.po +++ /dev/null @@ -1,179 +0,0 @@ -# Brazilian Portuguese translation for account-financial-tools -# Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014 -# This file is distributed under the same license as the account-financial-tools package. -# FIRST AUTHOR , 2014. -# -msgid "" -msgstr "" -"Project-Id-Version: account-financial-tools\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2013-10-18 17:52+0000\n" -"PO-Revision-Date: 2014-01-10 11:21+0000\n" -"Last-Translator: FULL NAME \n" -"Language-Team: Brazilian Portuguese \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-06-12 06:31+0000\n" -"X-Generator: Launchpad (build 17041)\n" - -#. module: currency_rate_update -#: view:res.company:0 -msgid "Currency auto update configuration" -msgstr "" - -#. module: currency_rate_update -#: view:res.company:0 -msgid "Currency updates services" -msgstr "" - -#. module: currency_rate_update -#: field:currency.rate.update.service,company_id:0 -msgid "linked company" -msgstr "" - -#. module: currency_rate_update -#: view:currency.rate.update.service:0 -msgid "Rate" -msgstr "" - -#. module: currency_rate_update -#: view:currency.rate.update.service:0 -msgid "Rates" -msgstr "" - -#. module: currency_rate_update -#: field:res.company,multi_company_currency_enable:0 -msgid "Multi company currency" -msgstr "" - -#. module: currency_rate_update -#: field:res.company,interval_type:0 -msgid "Currency update frequence" -msgstr "" - -#. module: currency_rate_update -#: selection:res.company,interval_type:0 -msgid "Day(s)" -msgstr "" - -#. module: currency_rate_update -#: field:currency.rate.update.service,currency_to_update:0 -msgid "currency to update with this service" -msgstr "" - -#. module: currency_rate_update -#: selection:currency.rate.update.service,service:0 -msgid "Narodowy Bank Polski" -msgstr "" - -#. module: currency_rate_update -#: field:currency.rate.update.service,note:0 -msgid "update notice" -msgstr "" - -#. module: currency_rate_update -#: field:currency.rate.update.service,service:0 -msgid "Webservice to use" -msgstr "" - -#. module: currency_rate_update -#: help:currency.rate.update.service,max_delta_days:0 -msgid "" -"If the time delta between the rate date given by the webservice and the " -"current date exeeds this value, then the currency rate is not updated in " -"OpenERP." -msgstr "" - -#. module: currency_rate_update -#: field:res.company,services_to_use:0 -msgid "Currency update services" -msgstr "" - -#. module: currency_rate_update -#: constraint:currency.rate.update.service:0 -msgid "'Max delta days' must be >= 0" -msgstr "" - -#. module: currency_rate_update -#: code:addons/currency_rate_update/currency_rate_update.py:89 -#: sql_constraint:currency.rate.update.service:0 -#, python-format -msgid "You can use a service one time per company !" -msgstr "" - -#. module: currency_rate_update -#: selection:currency.rate.update.service,service:0 -msgid "Yahoo Finance " -msgstr "" - -#. module: currency_rate_update -#: field:currency.rate.update.service,max_delta_days:0 -msgid "Max delta days" -msgstr "" - -#. module: currency_rate_update -#: selection:currency.rate.update.service,service:0 -msgid "Admin.ch" -msgstr "" - -#. module: currency_rate_update -#: model:ir.model,name:currency_rate_update.model_res_company -msgid "Companies" -msgstr "" - -#. module: currency_rate_update -#: view:currency.rate.update.service:0 -msgid "Currencies to update with this service" -msgstr "" - -#. module: currency_rate_update -#: selection:res.company,interval_type:0 -msgid "Week(s)" -msgstr "" - -#. module: currency_rate_update -#: help:res.company,multi_company_currency_enable:0 -msgid "" -"if this case is not check you can not set currency is active on two company" -msgstr "" - -#. module: currency_rate_update -#: code:addons/currency_rate_update/currency_rate_update.py:153 -#: model:ir.model,name:currency_rate_update.model_currency_rate_update -#: model:ir.model,name:currency_rate_update.model_currency_rate_update_service -#, python-format -msgid "Currency Rate Update" -msgstr "" - -#. module: currency_rate_update -#: view:currency.rate.update.service:0 -msgid "Logs" -msgstr "" - -#. module: currency_rate_update -#: field:res.company,auto_currency_up:0 -msgid "Automatical update of the currency this company" -msgstr "" - -#. module: currency_rate_update -#: selection:res.company,interval_type:0 -msgid "Month(s)" -msgstr "" - -#. module: currency_rate_update -#: selection:currency.rate.update.service,service:0 -msgid "European Central Bank" -msgstr "" - -#. module: currency_rate_update -#: help:res.company,interval_type:0 -msgid "" -"changing this value will\n" -" also affect other compagnies" -msgstr "" - -#. module: currency_rate_update -#: view:res.company:0 -msgid "Refresh currencies" -msgstr "" diff --git a/currency_rate_update/model/company.py b/currency_rate_update/model/company.py index 6a9533aec..3ac70a27a 100644 --- a/currency_rate_update/model/company.py +++ b/currency_rate_update/model/company.py @@ -44,12 +44,12 @@ class res_company(models.Model): # Activate the currency update auto_currency_up = fields.Boolean( - string="Automatic Update", + string='Automatic Update', help="Automatical update of the currencies for this company") # Function field that allows to know the # multi company currency implementation multi_company_currency_enable = fields.Boolean( - string="Multi company currency", + string='Multi company currency', translate=True, compute="_compute_multi_curr_enable", help="When this option is unchecked it will allow users " "to set a distinct currency updates on each company." diff --git a/currency_rate_update/model/currency_rate_update.py b/currency_rate_update/model/currency_rate_update.py index c0e471816..d22033e5d 100644 --- a/currency_rate_update/model/currency_rate_update.py +++ b/currency_rate_update/model/currency_rate_update.py @@ -125,8 +125,8 @@ class Currency_rate_update_service(models.Model): @api.onchange('interval_number') def _onchange_interval_number(self): if self.interval_number == 0: - self.note = "%s Service deactivated. Currencies will no longer " \ - "be updated. \n%s" % (fields.Datetime.now(), + self.note = '%s Service deactivated. Currencies will no longer ' \ + 'be updated. \n%s' % (fields.Datetime.now(), self.note and self.note or '') @api.onchange('service') @@ -167,9 +167,9 @@ class Currency_rate_update_service(models.Model): ('ECB_getter', 'European Central Bank'), ('YAHOO_getter', 'Yahoo Finance'), # Added for polish rates - ('PL_NBP_getter', 'Narodowy Bank Polski'), + ('PL_NBP_getter', 'National Bank of Poland'), # Added for mexican rates - ('MX_BdM_getter', 'Banco de México'), + ('MX_BdM_getter', 'Bank of Mexico'), # Bank of Canada is using RSS-CB # http://www.cbwiki.net/wiki/index.php/Specification_1.1 # This RSS format is used by other national banks @@ -178,27 +178,27 @@ class Currency_rate_update_service(models.Model): # Added for romanian rates ('RO_BNR_getter', 'National Bank of Romania') ], - "Webservice to use", + string="Webservice to use", required=True) # List of currencies available on webservice currency_list = fields.Many2many('res.currency', 'res_currency_update_avail_rel', 'service_id', 'currency_id', - 'Currencies available') + string='Currencies available') # List of currency to update currency_to_update = fields.Many2many('res.currency', 'res_currency_auto_update_rel', 'service_id', 'currency_id', - 'Currencies to update with this ' - 'service') + string='Currencies to update with ' + 'this service') # Link with company company_id = fields.Many2one('res.company', 'Linked Company') # Note fileds that will be used as a logger - note = fields.Text('Update notice') + note = fields.Text('Update logs') max_delta_days = fields.Integer( - 'Max delta days', default=4, required=True, + string='Max delta days', 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 OpenERP.") @@ -208,8 +208,8 @@ class Currency_rate_update_service(models.Model): ('months', 'Month(s)')], string='Currency update frequency', default='days') - interval_number = fields.Integer('Frequency', default=1) - next_run = fields.Date('Next run on', default=fields.Date.today()) + interval_number = fields.Integer(string='Frequency', default=1) + next_run = fields.Date(string='Next run on', default=fields.Date.today()) _sql_constraints = [('curr_service_unique', 'unique (service, company_id)', @@ -273,14 +273,14 @@ class Currency_rate_update_service(models.Model): rate_obj.create(vals) # Show the most recent note at the top - msg = "%s \n%s currency updated. %s" % ( + msg = '%s \n%s currency updated. %s' % ( log_info or '', fields.Datetime.to_string(datetime.today()), note ) self.write({'note': msg}) except Exception as exc: - error_msg = "\n%s ERROR : %s %s" % ( + error_msg = '\n%s ERROR : %s %s' % ( fields.Datetime.to_string(datetime.today()), repr(exc), note @@ -298,7 +298,7 @@ class Currency_rate_update_service(models.Model): @api.multi def run_currency_update(self): - "Update currency at the given frequence" + # Update currency at the given frequence services = self.search([('next_run', '=', fields.Date.today())]) services.with_context(cron=True).refresh_currency() diff --git a/currency_rate_update/view/company_view.xml b/currency_rate_update/view/company_view.xml index 2e33ca86a..60ebb8a27 100644 --- a/currency_rate_update/view/company_view.xml +++ b/currency_rate_update/view/company_view.xml @@ -6,12 +6,12 @@ - + - +