Merge lp:~atin81/account-financial-tools/account-financial-tools into lp:~account-core-editors/account-financial-tools/6.1

Proposed by Agustín
Status: Merged
Approved by: Yannick Vaucher @ Camptocamp
Approved revision: 105
Merged at revision: 107
Proposed branch: lp:~atin81/account-financial-tools/account-financial-tools
Merge into: lp:~account-core-editors/account-financial-tools/6.1
Diff against target: 1110 lines (+349/-293)
5 files modified
currency_rate_update/__init__.py (+30/-26)
currency_rate_update/__openerp__.py (+46/-41)
currency_rate_update/company.py (+66/-68)
currency_rate_update/currency_rate_update.py (+204/-155)
currency_rate_update/currency_rate_update.xml (+3/-3)
To merge this branch: bzr merge lp:~atin81/account-financial-tools/account-financial-tools
Reviewer Review Type Date Requested Status
Yannick Vaucher @ Camptocamp code review, no tests Approve
Omar (Pexego) code review Approve
Guewen Baconnier @ Camptocamp code review Approve
Nicolas Bessi - Camptocamp (community) code review, no tests Needs Fixing
Moisés López - http://www.vauxoo.com Pending
Review via email: mp+183064@code.launchpad.net

Commit message

I have corrected the comments you done, please review it and let me know if there is anything else needed for merge it.

Description of the change

Implemented feature for auto update currency exchange using Banxico information for USD & MXN

To post a comment you must log in.
Revision history for this message
Nicolas Bessi - Camptocamp (nbessi-c2c-deactivatedaccount) wrote :

Hello,

Thanks for your proposal

res_company class renaming is not a good thing it my break monkey patch or existing imports.
class does not need to be instanciated.

Line 427
Your change is faulty some it can induce false positive, to be reverted please.

netsvc.Logger is Deprecated please use logging module "logger.debug".

Regards

Nicolas

review: Needs Fixing (code review, no tests)
105. By Agustin Cruz <email address hidden>

Corrected Nicolas comments

Revision history for this message
Moisés López - http://www.vauxoo.com (moylop260) wrote :

Hello @Nicolas,
FYI FIX commited

Revision history for this message
Guewen Baconnier @ Camptocamp (gbaconnier-c2c) wrote :

Notes on this module:
 - the license is still GPL, should be moved to AGPL.
 - A cleaning should be done (propagation of context is missing, weird constructions or idioms).

That's not the matter of this MP though, so I approve.

Thanks.

review: Approve (code review)
Revision history for this message
Omar (Pexego) (omar7r) wrote :

LGTM

review: Approve (code review)
Revision history for this message
Yannick Vaucher @ Camptocamp (yvaucher-c2c) wrote :

Ola

+1 about Guewen comment

So I approve this MP for the feature addition for 'banco de mexico'

Cheers

review: Approve (code review, no tests)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'currency_rate_update/__init__.py' (properties changed: +x to -x)
--- currency_rate_update/__init__.py 2011-08-12 12:35:23 +0000
+++ currency_rate_update/__init__.py 2013-09-17 18:13:12 +0000
@@ -1,33 +1,37 @@
1##############################################################################1##############################################################################
2#2#
3# Copyright (c) 2008 Camtocamp SA3# Copyright (c) 2008 Camtocamp SA
4# @author JB Aubort, Nicolas Bessi, Joel Grand-Guillaume4# @author JB Aubort, Nicolas Bessi, Joel Grand-Guillaume
5# European Central Bank and Polish National Bank invented by Grzegorz Grzelak5# European Central Bank and Polish National Bank invented by Grzegorz Grzelak
6# $Id: $6# Banxico implemented by Agustin Cruz openpyme.mx
7#7# $Id: $
8# WARNING: This program as such is intended to be used by professional8#
9# programmers who take the whole responsability of assessing all potential9# WARNING: This program as such is intended to be used by professional
10# consequences resulting from its eventual inadequacies and bugs10# programmers who take the whole responsability of assessing all potential
11# End users who are looking for a ready-to-use solution with commercial11# consequences resulting from its eventual inadequacies and bugs
12# garantees and support are strongly adviced to contract a Free Software12# End users who are looking for a ready-to-use solution with commercial
13# Service Company13# garantees and support are strongly adviced to contract a Free Software
14#14# Service Company
15# This program is Free Software; you can redistribute it and/or15#
16# modify it under the terms of the GNU General Public License16# This program is Free Software; you can redistribute it and/or
17# as published by the Free Software Foundation; either version 217# modify it under the terms of the GNU General Public License
18# of the License, or (at your option) any later version.18# as published by the Free Software Foundation; either version 2
19#19# of the License, or (at your option) any later version.
20# This program is distributed in the hope that it will be useful,20#
21# but WITHOUT ANY WARRANTY; without even the implied warranty of21# This program is free software: you can redistribute it and/or modify
22# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the22# it under the terms of the GNU Affero General Public License as
23# GNU General Public License for more details.23# published by the Free Software Foundation, either version 3 of the
24#24# License, or (at your option) any later version.
25# You should have received a copy of the GNU General Public License25#
26# along with this program; if not, write to the Free Software26# This program is distributed in the hope that it will be useful,
27# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.27# but WITHOUT ANY WARRANTY; without even the implied warranty of
28# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29# GNU Affero General Public License for more details.
30#
31# You should have received a copy of the GNU Affero General Public License
32# along with this program. If not, see <http://www.gnu.org/licenses/>.
28#33#
29##############################################################################34##############################################################################
30
31import currency_rate_update35import currency_rate_update
32import company36import company
33import wizard37import wizard
3438
=== modified file 'currency_rate_update/__openerp__.py' (properties changed: +x to -x)
--- currency_rate_update/__openerp__.py 2012-07-31 14:29:55 +0000
+++ currency_rate_update/__openerp__.py 2013-09-17 18:13:12 +0000
@@ -1,30 +1,35 @@
1##############################################################################1##############################################################################
2#2#
3# Copyright (c) 2008 Camtocamp SA3# Copyright (c) 2008 Camtocamp SA
4# @author JB Aubort, Nicolas Bessi, Joel Grand-Guillaume4# @author JB Aubort, Nicolas Bessi, Joel Grand-Guillaume
5# European Central Bank and Polish National Bank invented by Grzegorz Grzelak5# European Central Bank and Polish National Bank invented by Grzegorz Grzelak
6# $Id: $6# Banxico implemented by Agustin Cruz openpyme.mx
7#7# $Id: $
8# WARNING: This program as such is intended to be used by professional8#
9# programmers who take the whole responsability of assessing all potential9# WARNING: This program as such is intended to be used by professional
10# consequences resulting from its eventual inadequacies and bugs10# programmers who take the whole responsability of assessing all potential
11# End users who are looking for a ready-to-use solution with commercial11# consequences resulting from its eventual inadequacies and bugs
12# garantees and support are strongly adviced to contract a Free Software12# End users who are looking for a ready-to-use solution with commercial
13# Service Company13# garantees and support are strongly adviced to contract a Free Software
14#14# Service Company
15# This program is Free Software; you can redistribute it and/or15#
16# modify it under the terms of the GNU General Public License16# This program is Free Software; you can redistribute it and/or
17# as published by the Free Software Foundation; either version 217# modify it under the terms of the GNU General Public License
18# of the License, or (at your option) any later version.18# as published by the Free Software Foundation; either version 2
19#19# of the License, or (at your option) any later version.
20# This program is distributed in the hope that it will be useful,20#
21# but WITHOUT ANY WARRANTY; without even the implied warranty of21# This program is free software: you can redistribute it and/or modify
22# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the22# it under the terms of the GNU Affero General Public License as
23# GNU General Public License for more details.23# published by the Free Software Foundation, either version 3 of the
24#24# License, or (at your option) any later version.
25# You should have received a copy of the GNU General Public License25#
26# along with this program; if not, write to the Free Software26# This program is distributed in the hope that it will be useful,
27# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.27# but WITHOUT ANY WARRANTY; without even the implied warranty of
28# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29# GNU Affero General Public License for more details.
30#
31# You should have received a copy of the GNU Affero General Public License
32# along with this program. If not, see <http://www.gnu.org/licenses/>.
28#33#
29##############################################################################34##############################################################################
30{35{
@@ -33,9 +38,8 @@
33 "author" : "Camptocamp",38 "author" : "Camptocamp",
34 "website" : "http://camptocamp.com",39 "website" : "http://camptocamp.com",
35 "category" : "Financial Management/Configuration",40 "category" : "Financial Management/Configuration",
36 "description": """Import exchange rates from the Internet.41 "description": """
3742Import exchange rates from three different sources on the internet :
38The module is able to use 4 different sources:
3943
401. Admin.ch441. Admin.ch
41 Updated daily, source in CHF.45 Updated daily, source in CHF.
@@ -51,36 +55,37 @@
5155
524. Polish National Bank (Narodowy Bank Polski) (contribution by Grzegorz Grzelak)564. Polish National Bank (Narodowy Bank Polski) (contribution by Grzegorz Grzelak)
53 Takes official rates from www.nbp.pl. Adds rate table symbol in log.57 Takes official rates from www.nbp.pl. Adds rate table symbol in log.
54 You should check when rates should apply to bookkeeping. If next day you should58 You should check when rates should apply to bookkeeping. If next day you should
55 change the update hour in schedule settings because in OpenERP they apply from59 change the update hour in schedule settings because in OpenERP they apply from
56 date of update (date - no hours).60 date of update (date - no hours).
61
625. Banxico for USD & MXN (created by Agustín Cruz)
63 Updated daily
5764
58In the roadmap : Google Finance.65In the roadmap : Google Finance.
59 Updated daily from Citibank N.A., source in EUR. Information may be delayed.66 Updated daily from Citibank N.A., source in EUR. Information may be delayed.
60 This is parsed from an HTML page, so it may be broken at anytime.67 This is parsed from an HTML page, so it may be broken at anytime.
6168
62The update can be set under the company form.69The update can be set under de company form.
63You can set for each services which currency you want to update.70You can set for each services which currency you want to update.
64The logs of the update are visible under the service note.71The log of the update are visible under the service note.
65You can active or deactivate the update.72You can active or deactivate the update.
66The module uses internal ir_cron feature from OpenERP, so the job is launched once73The module uses internal ir_cron feature from OpenERP, so the job is launched once
67the server starts if the 'first execute date' is before the current day.74the server starts if the 'first execute date' is before the current day.
68The module supports multi-company currency in two ways:75The module supports multi-company currency in two way :
6976 the currencies are shared, you can set currency update only on one
70* the currencies are shared, you can set currency update only on one
71 company77 company
72* the currency are separated, you can set currency on every company78 the currency are separated, you can set currency on every company
73 separately79 separately
7480A function field let you know your currency configuration.
75A function field lets you know your currency configuration.
7681
77If in multi-company mode, the base currency will be the first company's currency82If in multi-company mode, the base currency will be the first company's currency
78found in database.83found in database.
7984
80Thanks to main contributors: Grzegorz Grzelak, Alexis de Lattre85
86Special thanks and contribs to other Main contributor: Grzegorz Grzelak, Alexis de Lattre
81""",87""",
82 "depends" : ["base",88 "depends" : ["base", "account"],
83 "account"], #Added to ensure account security groups are present
84 "init_xml" : ["security/security.xml"],89 "init_xml" : ["security/security.xml"],
85 "update_xml" : [90 "update_xml" : [
86 "currency_rate_update.xml",91 "currency_rate_update.xml",
8792
=== modified file 'currency_rate_update/company.py'
--- currency_rate_update/company.py 2012-02-03 13:53:02 +0000
+++ currency_rate_update/company.py 2013-09-17 18:13:12 +0000
@@ -1,50 +1,48 @@
1# -*- encoding: utf-8 -*-1# -*- encoding: utf-8 -*-
2# company.py2# company.py
3# c2c_currency_update3# c2c_currency_update
4# @author Nicolas Bessi 4# @author Nicolas Bessi
5# Copyright (c) 2009 CamptoCamp. All rights reserved.5# Copyright (c) 2009 CamptoCamp. All rights reserved.
6##############################################################################6#
7#7# WARNING: This program as such is intended to be used by professional
8# WARNING: This program as such is intended to be used by professional8# programmers who take the whole responsability of assessing all potential
9# programmers who take the whole responsability of assessing all potential9# consequences resulting from its eventual inadequacies and bugs
10# consequences resulting from its eventual inadequacies and bugs10# End users who are looking for a ready-to-use solution with commercial
11# End users who are looking for a ready-to-use solution with commercial11# garantees and support are strongly adviced to contract a Free Software
12# garantees and support are strongly adviced to contract a Free Software12# Service Company
13# Service Company13#
14#14# This program is free software: you can redistribute it and/or modify
15# This program is Free Software; you can redistribute it and/or15# it under the terms of the GNU Affero General Public License as
16# modify it under the terms of the GNU General Public License16# published by the Free Software Foundation, either version 3 of the
17# as published by the Free Software Foundation; either version 217# License, or (at your option) any later version.
18# of the License, or (at your option) any later version.18#
19#19# This program is distributed in the hope that it will be useful,
20# This program is distributed in the hope that it will be useful,20# but WITHOUT ANY WARRANTY; without even the implied warranty of
21# but WITHOUT ANY WARRANTY; without even the implied warranty of21# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the22# GNU Affero General Public License for more details.
23# GNU General Public License for more details.23#
24#24# You should have received a copy of the GNU Affero General Public License
25# You should have received a copy of the GNU General Public License25# along with this program. If not, see <http://www.gnu.org/licenses/>.
26# along with this program; if not, write to the Free Software
27# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28#26#
29##############################################################################27##############################################################################
3028
31import netsvc29import netsvc
32from osv import fields, osv30from osv import fields, osv
33class res_company(osv.osv):31class res_company(osv.osv):
34 """override company to add currency update"""32 """Override company to add currency update"""
35 33
36 def _multi_curr_enable(self, cr, uid, ids, field_name, arg, context={}):34 def _multi_curr_enable(self, cr, uid, ids, field_name, arg, context={}):
37 "check if multi company currency is enabled"35 "check if multi company currency is enabled"
38 result = {}36 result = {}
39 if self.pool.get('ir.model.fields').search(cr, uid, [('name', '=', 'company_id'), ('model', '=', 'res.currency')])==[]:37 if self.pool.get('ir.model.fields').search(cr, uid, [('name', '=', 'company_id'), ('model', '=', 'res.currency')]) == []:
40 enable = 038 enable = 0
41 else:39 else:
42 enable = 140 enable = 1
43 for id in ids:41 for id in ids:
44 result[id] = enable42 result[id] = enable
45 return result43 return result
46 44
47 45
48 def button_refresh_currency(self, cr, uid, ids, context=None):46 def button_refresh_currency(self, cr, uid, ids, context=None):
49 """Refrech the currency !!for all the company47 """Refrech the currency !!for all the company
50 now"""48 now"""
@@ -54,8 +52,8 @@
54 except Exception, e:52 except Exception, e:
55 return False53 return False
56 return True54 return True
57 55
58 56
59 def on_change_auto_currency_up(self, cr, uid, id, value):57 def on_change_auto_currency_up(self, cr, uid, id, value):
60 """handle the activation of the currecny update on compagnies.58 """handle the activation of the currecny update on compagnies.
61 There are two ways of implementing multi_company currency, 59 There are two ways of implementing multi_company currency,
@@ -65,24 +63,24 @@
65 object running. 63 object running.
66 If yours currency are not share you will be able to activate the64 If yours currency are not share you will be able to activate the
67 auto update on each separated company"""65 auto update on each separated company"""
68 66
69 if len(id) :67 if len(id) :
70 id = id[0]68 id = id[0]
71 else :69 else :
72 return {}70 return {}
73 enable = self.browse(cr, uid, id).multi_company_currency_enable71 enable = self.browse(cr, uid, id).multi_company_currency_enable
74 compagnies = self.search(cr, uid, [])72 compagnies = self.search(cr, uid, [])
75 activate_cron = 'f'73 activate_cron = 'f'
76 if not value :74 if not value :
77 # this statement is here beacaus we do no want to save in case of error75 # this statement is here beacaus we do no want to save in case of error
78 self.write(cr, uid, id,{'auto_currency_up':value})76 self.write(cr, uid, id, {'auto_currency_up':value})
79 for comp in compagnies :77 for comp in compagnies :
80 if self.browse(cr, uid, comp).auto_currency_up: 78 if self.browse(cr, uid, comp).auto_currency_up:
81 activate_cron = 't'79 activate_cron = 't'
82 break80 break
83 self.pool.get('currency.rate.update').save_cron(81 self.pool.get('currency.rate.update').save_cron(
84 cr, 82 cr,
85 uid, 83 uid,
86 {'active':activate_cron}84 {'active':activate_cron}
87 )85 )
88 return {}86 return {}
@@ -90,75 +88,75 @@
90 for comp in compagnies :88 for comp in compagnies :
91 if comp != id and not enable:89 if comp != id and not enable:
92 if self.browse(cr, uid, comp).multi_company_currency_enable:90 if self.browse(cr, uid, comp).multi_company_currency_enable:
93 #we ensure taht we did not have write a true value91 # we ensure taht we did not have write a true value
94 self.write(cr, uid, id,{'auto_currency_up':False})92 self.write(cr, uid, id, {'auto_currency_up':False})
95 return {93 return {
96 'value':{ 94 'value':{
97 'auto_currency_up':False95 'auto_currency_up':False
98 },96 },
99 97
100 'warning':{98 'warning':{
101 'title':"Warning",99 'title':"Warning",
102 'message': 'Yon can not activate auto currency '+\100 'message': 'Yon can not activate auto currency ' + \
103 'update on more thant one company with this '+101 'update on more thant one company with this ' +
104 'multi company configuration'102 'multi company configuration'
105 }103 }
106 }104 }
107 self.write(cr, uid, id,{'auto_currency_up':value})105 self.write(cr, uid, id, {'auto_currency_up':value})
108 for comp in compagnies :106 for comp in compagnies :
109 if self.browse(cr, uid, comp).auto_currency_up: 107 if self.browse(cr, uid, comp).auto_currency_up:
110 activate_cron = 't'108 activate_cron = 't'
111 self.pool.get('currency.rate.update').save_cron(109 self.pool.get('currency.rate.update').save_cron(
112 cr, 110 cr,
113 uid, 111 uid,
114 {'active':activate_cron}112 {'active':activate_cron}
115 )113 )
116 break114 break
117 return {}115 return {}
118 116
119 117
120 def on_change_intervall(self, cr, uid, id, interval) :118 def on_change_intervall(self, cr, uid, id, interval) :
121 ###Function that will update the cron119 # ##Function that will update the cron
122 ###freqeuence120 # ##freqeuence
123 self.pool.get('currency.rate.update').save_cron(121 self.pool.get('currency.rate.update').save_cron(
124 cr, 122 cr,
125 uid, 123 uid,
126 {'interval_type':interval}124 {'interval_type':interval}
127 )125 )
128 compagnies = self.search(cr, uid, [])126 compagnies = self.search(cr, uid, [])
129 for comp in compagnies :127 for comp in compagnies :
130 self.write(cr, uid, comp,{'interval_type':interval})128 self.write(cr, uid, comp, {'interval_type':interval})
131 return {}129 return {}
132 130
133 _inherit = "res.company"131 _inherit = "res.company"
134 _columns = {132 _columns = {
135 ### activate the currency update133 # ## activate the currency update
136 'auto_currency_up': fields.boolean('Automatical update of the currency this company'),134 'auto_currency_up': fields.boolean('Automatical update of the currency this company'),
137 'services_to_use' : fields.one2many(135 'services_to_use' : fields.one2many(
138 'currency.rate.update.service', 136 'currency.rate.update.service',
139 'company_id',137 'company_id',
140 'Currency update services' 138 'Currency update services'
141 ),139 ),
142 ###predifine cron frequence140 # ##predifine cron frequence
143 'interval_type': fields.selection(141 'interval_type': fields.selection(
144 [142 [
145 ('days','Day(s)'), 143 ('days', 'Day(s)'),
146 ('weeks', 'Week(s)'), 144 ('weeks', 'Week(s)'),
147 ('months', 'Month(s)')145 ('months', 'Month(s)')
148 ],146 ],
149 'Currency update frequence',147 'Currency update frequence',
150 help="""changing this value will148 help="""changing this value will
151 also affect other compagnies"""149 also affect other compagnies"""
152 ),150 ),
153 ###function field that allows to know the151 # ##function field that allows to know the
154 ###mutli company currency implementation 152 # ##mutli company currency implementation
155 'multi_company_currency_enable' : fields.function(153 'multi_company_currency_enable' : fields.function(
156 _multi_curr_enable, 154 _multi_curr_enable,
157 method=True, 155 method=True,
158 type='boolean', 156 type='boolean',
159 string="Multi company currency",157 string="Multi company currency",
160 help='if this case is not check you can'+\158 help='if this case is not check you can' + \
161 ' not set currency is active on two company'159 ' not set currency is active on two company'
162 ),160 ),
163 } 161 }
164res_company()162res_company()
165163
=== modified file 'currency_rate_update/company_view.xml' (properties changed: +x to -x)
=== modified file 'currency_rate_update/currency_rate_update.py' (properties changed: +x to -x)
--- currency_rate_update/currency_rate_update.py 2012-04-23 12:19:50 +0000
+++ currency_rate_update/currency_rate_update.py 2013-09-17 18:13:12 +0000
@@ -2,8 +2,8 @@
2##############################################################################2##############################################################################
3#3#
4# Copyright (c) 2009 Camptocamp SA4# Copyright (c) 2009 Camptocamp SA
5# @author Nicolas Bessi 5# @author Nicolas Bessi
6# @source JBA and AWST inpiration 6# @source JBA and AWST inpiration
7# @contributor Grzegorz Grzelak (grzegorz.grzelak@birdglobe.com), Joel Grand-Guillaume7# @contributor Grzegorz Grzelak (grzegorz.grzelak@birdglobe.com), Joel Grand-Guillaume
8# Copyright (c) 2010 Alexis de Lattre (alexis@via.ecp.fr)8# Copyright (c) 2010 Alexis de Lattre (alexis@via.ecp.fr)
9# - ported XML-based webservices (Admin.ch, ECB, PL NBP) to new XML lib9# - ported XML-based webservices (Admin.ch, ECB, PL NBP) to new XML lib
@@ -54,20 +54,19 @@
54 _name = "currency.rate.update.service"54 _name = "currency.rate.update.service"
55 _description = "Currency Rate Update"55 _description = "Currency Rate Update"
56 _columns = {56 _columns = {
57 ##list of webservicies the value sould be a class name 57 # #list of webservicies the value sould be a class name
58 'service' : fields.selection( 58 'service' : fields.selection(
59 [59 [
60 ('Admin_ch_getter','Admin.ch'),60 ('Admin_ch_getter', 'Admin.ch'),
61 ('ECB_getter','European Central Bank'),61 ('ECB_getter', 'European Central Bank'),
62 #('NYFB_getter','Federal Reserve Bank of NY'), 62 ('Yahoo_getter', 'Yahoo Finance '),
63 #('Google_getter','Google Finance'),63 ('PL_NBP_getter', 'Narodowy Bank Polski'), # Added for polish rates
64 ('Yahoo_getter','Yahoo Finance '),64 ('Banxico_getter', 'Banco de México'), # Added for mexican rates
65 ('PL_NBP_getter','Narodowy Bank Polski'), # Added for polish rates
66 ],65 ],
67 "Webservice to use",66 "Webservice to use",
68 required = True67 required=True
69 ),68 ),
70 ##list of currency to update 69 # #list of currency to update
71 'currency_to_update' : fields.many2many(70 'currency_to_update' : fields.many2many(
72 'res.currency',71 'res.currency',
73 'res_curreny_auto_udate_rel',72 'res_curreny_auto_udate_rel',
@@ -75,12 +74,12 @@
75 'currency_id',74 'currency_id',
76 'currency to update with this service',75 'currency to update with this service',
77 ),76 ),
78 #back ref 77 # back ref
79 'company_id' : fields.many2one(78 'company_id' : fields.many2one(
80 'res.company',79 'res.company',
81 'linked company',80 'linked company',
82 ),81 ),
83 ##note fileds that will be used as a logger82 # #note fileds that will be used as a logger
84 'note':fields.text('update notice'),83 'note':fields.text('update notice'),
85 '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."),84 '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."),
86 }85 }
@@ -89,8 +88,8 @@
89 }88 }
90 _sql_constraints = [89 _sql_constraints = [
91 (90 (
92 'curr_service_unique', 91 'curr_service_unique',
93 'unique (service, company_id)', 92 'unique (service, company_id)',
94 _('You can use a service one time per company !')93 _('You can use a service one time per company !')
95 )94 )
96 ]95 ]
@@ -114,63 +113,63 @@
114 update currencies based on a web url"""113 update currencies based on a web url"""
115 _name = "currency.rate.update"114 _name = "currency.rate.update"
116 _description = "Currency Rate Update"115 _description = "Currency Rate Update"
117 ##dict that represent a cron object116 # #dict that represent a cron object
118 cron = {117 cron = {
119 'active' : False,118 'active' : False,
120 'priority' : 1,119 'priority' : 1,
121 'interval_number' : 1,120 'interval_number' : 1,
122 'interval_type' : 'weeks',121 'interval_type' : 'weeks',
123 'nextcall' : time.strftime("%Y-%m-%d %H:%M:%S", (datetime.today() + timedelta(days=1)).timetuple() ), #tomorrow same time122 'nextcall' : time.strftime("%Y-%m-%d %H:%M:%S", (datetime.today() + timedelta(days=1)).timetuple()), # tomorrow same time
124 'numbercall' : -1,123 'numbercall' :-1,
125 'doall' : True,124 'doall' : True,
126 'model' : 'currency.rate.update',125 'model' : 'currency.rate.update',
127 'function' : 'run_currency_update',126 'function' : 'run_currency_update',
128 'args' : '()', 127 'args' : '()',
129 }128 }
130 129
131 logger = netsvc.Logger()130 logger = logging.getLogger(__name__)
132 LOG_NAME = 'cron-rates'131 LOG_NAME = 'cron-rates'
133 MOD_NAME = 'c2c_currency_rate_update: '132 MOD_NAME = 'currency_rate_update: '
134 def get_cron_id(self, cr, uid, context):133 def get_cron_id(self, cr, uid, context):
135 """return the updater cron's id. Create one if the cron does not exists """134 """return the updater cron's id. Create one if the cron does not exists """
136 135
137 cron_id = 0136 cron_id = 0
138 cron_obj = self.pool.get('ir.cron')137 cron_obj = self.pool.get('ir.cron')
139 try: 138 try:
140 #find the cron that send messages139 # find the cron that send messages
141 cron_id = cron_obj.search(140 cron_id = cron_obj.search(
142 cr, 141 cr,
143 uid, 142 uid,
144 [143 [
145 ('function', 'ilike', self.cron['function']), 144 ('function', 'ilike', self.cron['function']),
146 ('model', 'ilike', self.cron['model'])145 ('model', 'ilike', self.cron['model'])
147 ], 146 ],
148 context={147 context={
149 'active_test': False148 'active_test': False
150 } 149 }
151 )150 )
152 cron_id = int(cron_id[0])151 cron_id = int(cron_id[0])
153 except Exception,e :152 except Exception, e :
154 self.logger.notifyChannel(153 self.logger.notifyChannel(
155 self.LOG_NAME, 154 self.LOG_NAME,
156 netsvc.LOG_INFO, 155 netsvc.LOG_INFO,
157 'warning cron not found one will be created'156 'warning cron not found one will be created'
158 )157 )
159 pass # ignore if the cron is missing cause we are going to create it in db158 pass # ignore if the cron is missing cause we are going to create it in db
160 159
161 #the cron does not exists160 # the cron does not exists
162 if not cron_id :161 if not cron_id :
163 #translate162 # translate
164 self.cron['name'] = _('Currency Rate Update')163 self.cron['name'] = _('Currency Rate Update')
165 cron_id = cron_obj.create(cr, uid, self.cron, context)164 cron_id = cron_obj.create(cr, uid, self.cron, context)
166 165
167 return cron_id166 return cron_id
168 167
169 def save_cron(self, cr, uid, datas, context={}):168 def save_cron(self, cr, uid, datas, context={}):
170 """save the cron config data should be a dict"""169 """save the cron config data should be a dict"""
171 #modify the cron170 # modify the cron
172 cron_id = self.get_cron_id(cr, uid, context) 171 cron_id = self.get_cron_id(cr, uid, context)
173 result = self.pool.get('ir.cron').write(cr, uid, [cron_id], datas) 172 result = self.pool.get('ir.cron').write(cr, uid, [cron_id], datas)
174173
175 def run_currency_update(self, cr, uid):174 def run_currency_update(self, cr, uid):
176 "update currency at the given frequence"175 "update currency at the given frequence"
@@ -179,24 +178,24 @@
179 rate_obj = self.pool.get('res.currency.rate')178 rate_obj = self.pool.get('res.currency.rate')
180 companies = self.pool.get('res.company').search(cr, uid, [])179 companies = self.pool.get('res.company').search(cr, uid, [])
181 for comp in self.pool.get('res.company').browse(cr, uid, companies):180 for comp in self.pool.get('res.company').browse(cr, uid, companies):
182 ##the multi company currency can beset or no so we handle 181 # #the multi company currency can beset or no so we handle
183 ##the two case182 # #the two case
184 if not comp.auto_currency_up :183 if not comp.auto_currency_up :
185 continue184 continue
186 #we initialise the multi compnay search filter or not serach filter185 # we initialise the multi compnay search filter or not serach filter
187 search_filter = []186 search_filter = []
188 if comp.multi_company_currency_enable :187 if comp.multi_company_currency_enable :
189 search_filter = [('company_id','=',comp.id)]188 search_filter = [('company_id', '=', comp.id)]
190 #we fetch the main currency. The main rate should be set at 1.00189 # we fetch the main currency. The main rate should be set at 1.00
191 main_curr = comp.currency_id.name190 main_curr = comp.currency_id.name
192 for service in comp.services_to_use :191 for service in comp.services_to_use :
193 print "comp.services_to_use =", comp.services_to_use192 logger.debug("comp.services_to_use = %s" % (comp.services_to_use))
194 note = service.note or ''193 note = service.note or ''
195 try :194 try :
196 ## we initalize the class that will handle the request195 # # we initalize the class that will handle the request
197 ## and return a dict of rate196 # # and return a dict of rate
198 getter = factory.register(service.service)197 getter = factory.register(service.service)
199 print "getter =", getter198 logger.debug("getter = %s" % (type(getter)))
200 curr_to_fetch = map(lambda x : x.name, service.currency_to_update)199 curr_to_fetch = map(lambda x : x.name, service.currency_to_update)
201 res, log_info = getter.get_updated_currency(curr_to_fetch, main_curr, service.max_delta_days)200 res, log_info = getter.get_updated_currency(curr_to_fetch, main_curr, service.max_delta_days)
202 rate_name = time.strftime('%Y-%m-%d')201 rate_name = time.strftime('%Y-%m-%d')
@@ -215,27 +214,27 @@
215 'rate':res[curr.name],214 'rate':res[curr.name],
216 'name': rate_name215 'name': rate_name
217 }216 }
218 rate_obj.create( 217 rate_obj.create(
219 cr,218 cr,
220 uid,219 uid,
221 vals,220 vals,
222 )221 )
223 222
224 note = note + "\n%s currency updated. "\223 note = note + "\n%s currency updated. "\
225 %(datetime.strftime(datetime.today(), '%Y-%m-%d %H:%M:%S'))224 % (datetime.strftime(datetime.today(), '%Y-%m-%d %H:%M:%S'))
226 note = note + (log_info or '')225 note = note + (log_info or '')
227 service.write({'note':note})226 service.write({'note':note})
228 except Exception, e:227 except Exception, e:
229 error_msg = note + "\n%s ERROR : %s"\228 error_msg = note + "\n%s ERROR : %s"\
230 %(datetime.strftime(datetime.today(), '%Y-%m-%d %H:%M:%S'), str(e))229 % (datetime.strftime(datetime.today(), '%Y-%m-%d %H:%M:%S'), str(e))
231 self.logger.notifyChannel(self.LOG_NAME, netsvc.LOG_INFO, str(e))230 self.logger.notifyChannel(self.LOG_NAME, netsvc.LOG_INFO, str(e))
232 service.write({'note':error_msg})231 service.write({'note':error_msg})
233 232
234 233
235Currency_rate_update()234Currency_rate_update()
236235
237### Error Definition as specified in python 2.6 PEP236# ## Error Definition as specified in python 2.6 PEP
238class AbstractClassError(Exception): 237class AbstractClassError(Exception):
239 def __str__(self):238 def __str__(self):
240 return 'Abstract Class'239 return 'Abstract Class'
241 def __repr__(self):240 def __repr__(self):
@@ -247,25 +246,25 @@
247 def __repr__(self):246 def __repr__(self):
248 return 'Abstract Method'247 return 'Abstract Method'
249248
250class UnknowClassError(Exception): 249class UnknowClassError(Exception):
251 def __str__(self):250 def __str__(self):
252 return 'Unknown Class'251 return 'Unknown Class'
253 def __repr__(self):252 def __repr__(self):
254 return 'Unknown Class'253 return 'Unknown Class'
255class UnsuportedCurrencyError(Exception): 254class UnsuportedCurrencyError(Exception):
256 def __init__(self, value):255 def __init__(self, value):
257 self.curr = value256 self.curr = value
258 def __str__(self):257 def __str__(self):
259 return 'Unsupported currency '+self.curr258 return 'Unsupported currency ' + self.curr
260 def __repr__(self):259 def __repr__(self):
261 return 'Unsupported currency '+self.curr260 return 'Unsupported currency ' + self.curr
262 261
263### end of error definition 262# ## end of error definition
264class Currency_getter_factory():263class Currency_getter_factory():
265 """Factory pattern class that will return 264 """Factory pattern class that will return
266 a currency getter class base on the name passed265 a currency getter class base on the name passed
267 to the register method"""266 to the register method"""
268 def register(self, class_name): 267 def register(self, class_name):
269 allowed = [268 allowed = [
270 'Admin_ch_getter',269 'Admin_ch_getter',
271 'PL_NBP_getter',270 'PL_NBP_getter',
@@ -273,66 +272,67 @@
273 'NYFB_getter',272 'NYFB_getter',
274 'Google_getter',273 'Google_getter',
275 'Yahoo_getter',274 'Yahoo_getter',
275 'Banxico_getter'
276 ]276 ]
277 if class_name in allowed:277 if class_name in allowed:
278 class_def = eval(class_name)278 class_def = eval(class_name)
279 return class_def()279 return class_def()
280 else :280 else :
281 raise UnknowClassError281 raise UnknowClassError
282 282
283283
284class Curreny_getter_interface(object) :284class Curreny_getter_interface(object) :
285 "Abstract class of currency getter"285 "Abstract class of currency getter"
286 286
287 #remove in order to have a dryer code287 # remove in order to have a dryer code
288 # def __init__(self):288 # def __init__(self):
289 # raise AbstractClassError 289 # raise AbstractClassError
290 290
291 log_info = " "291 log_info = " "
292 292
293 supported_currency_array = \293 supported_currency_array = \
294['AFN', 'ALL', 'DZD', 'USD', 'USD', 'USD', 'EUR', 'AOA', 'XCD', 'XCD', 'ARS',294['AFN', 'ALL', 'DZD', 'USD', 'USD', 'USD', 'EUR', 'AOA', 'XCD', 'XCD', 'ARS',
295'AMD', 'AWG', 'AUD', 'EUR', 'AZN', 'EUR', 'BSD', 'BHD', 'EUR', 'BDT', 'BBD', 295'AMD', 'AWG', 'AUD', 'EUR', 'AZN', 'EUR', 'BSD', 'BHD', 'EUR', 'BDT', 'BBD',
296'XCD', 'BYR', 'EUR', 'BZD', 'XOF', 'BMD', 'BTN', 'INR', 'BOB', 'ANG', 'BAM', 296'XCD', 'BYR', 'EUR', 'BZD', 'XOF', 'BMD', 'BTN', 'INR', 'BOB', 'ANG', 'BAM',
297'BWP', 'NOK', 'BRL', 'GBP', 'USD', 'USD', 'BND', 'BGN', 'XOF', 'MMK', 'BIF', 297'BWP', 'NOK', 'BRL', 'GBP', 'USD', 'USD', 'BND', 'BGN', 'XOF', 'MMK', 'BIF',
298'XOF', 'USD', 'KHR', 'XAF', 'CAD', 'EUR', 'CVE', 'KYD', 'XAF', 'XAF', 'CLP', 298'XOF', 'USD', 'KHR', 'XAF', 'CAD', 'EUR', 'CVE', 'KYD', 'XAF', 'XAF', 'CLP',
299'CNY', 'AUD', 'AUD', 'COP', 'XAF', 'KMF', 'XPF', 'XAF', 'CDF', 'NZD', 'CRC', 299'CNY', 'AUD', 'AUD', 'COP', 'XAF', 'KMF', 'XPF', 'XAF', 'CDF', 'NZD', 'CRC',
300'HRK', 'CUP', 'ANG', 'EUR', 'CYP', 'CZK', 'DKK', 'DJF', 'XCD', 'DOP', 'EUR', 300'HRK', 'CUP', 'ANG', 'EUR', 'CYP', 'CZK', 'DKK', 'DJF', 'XCD', 'DOP', 'EUR',
301'XCD', 'IDR', 'USD', 'EGP', 'EUR', 'SVC', 'USD', 'GBP', 'XAF', 'ETB', 'ERN', 301'XCD', 'IDR', 'USD', 'EGP', 'EUR', 'SVC', 'USD', 'GBP', 'XAF', 'ETB', 'ERN',
302'EEK', 'ETB', 'EUR', 'FKP', 'DKK', 'FJD', 'EUR', 'EUR', 'EUR', 'XPF', 'XPF', 302'EEK', 'ETB', 'EUR', 'FKP', 'DKK', 'FJD', 'EUR', 'EUR', 'EUR', 'XPF', 'XPF',
303'EUR', 'XPF', 'XAF', 'GMD', 'GEL', 'EUR', 'GHS', 'GIP', 'XAU', 'GBP', 'EUR', 303'EUR', 'XPF', 'XAF', 'GMD', 'GEL', 'EUR', 'GHS', 'GIP', 'XAU', 'GBP', 'EUR',
304'DKK', 'XCD', 'XCD', 'EUR', 'USD', 'GTQ', 'GGP', 'GNF', 'XOF', 'GYD', 'HTG', 304'DKK', 'XCD', 'XCD', 'EUR', 'USD', 'GTQ', 'GGP', 'GNF', 'XOF', 'GYD', 'HTG',
305'USD', 'AUD', 'BAM', 'EUR', 'EUR', 'HNL', 'HKD', 'HUF', 'ISK', 'INR', 'IDR', 305'USD', 'AUD', 'BAM', 'EUR', 'EUR', 'HNL', 'HKD', 'HUF', 'ISK', 'INR', 'IDR',
306'XDR', 'IRR', 'IQD', 'EUR', 'IMP', 'ILS', 'EUR', 'JMD', 'NOK', 'JPY', 'JEP', 306'XDR', 'IRR', 'IQD', 'EUR', 'IMP', 'ILS', 'EUR', 'JMD', 'NOK', 'JPY', 'JEP',
307'JOD', 'KZT', 'AUD', 'KES', 'AUD', 'KPW', 'KRW', 'KWD', 'KGS', 'LAK', 'LVL', 307'JOD', 'KZT', 'AUD', 'KES', 'AUD', 'KPW', 'KRW', 'KWD', 'KGS', 'LAK', 'LVL',
308'LBP', 'LSL', 'ZAR', 'LRD', 'LYD', 'CHF', 'LTL', 'EUR', 'MOP', 'MKD', 'MGA', 308'LBP', 'LSL', 'ZAR', 'LRD', 'LYD', 'CHF', 'LTL', 'EUR', 'MOP', 'MKD', 'MGA',
309'EUR', 'MWK', 'MYR', 'MVR', 'XOF', 'EUR', 'MTL', 'FKP', 'USD', 'USD', 'EUR', 309'EUR', 'MWK', 'MYR', 'MVR', 'XOF', 'EUR', 'MTL', 'FKP', 'USD', 'USD', 'EUR',
310'MRO', 'MUR', 'EUR', 'AUD', 'MXN', 'USD', 'USD', 'EUR', 'MDL', 'EUR', 'MNT', 310'MRO', 'MUR', 'EUR', 'AUD', 'MXN', 'USD', 'USD', 'EUR', 'MDL', 'EUR', 'MNT',
311'EUR', 'XCD', 'MAD', 'MZN', 'MMK', 'NAD', 'ZAR', 'AUD', 'NPR', 'ANG', 'EUR', 311'EUR', 'XCD', 'MAD', 'MZN', 'MMK', 'NAD', 'ZAR', 'AUD', 'NPR', 'ANG', 'EUR',
312'XCD', 'XPF', 'NZD', 'NIO', 'XOF', 'NGN', 'NZD', 'AUD', 'USD', 'NOK', 'OMR', 312'XCD', 'XPF', 'NZD', 'NIO', 'XOF', 'NGN', 'NZD', 'AUD', 'USD', 'NOK', 'OMR',
313'PKR', 'USD', 'XPD', 'PAB', 'USD', 'PGK', 'PYG', 'PEN', 'PHP', 'NZD', 'XPT', 313'PKR', 'USD', 'XPD', 'PAB', 'USD', 'PGK', 'PYG', 'PEN', 'PHP', 'NZD', 'XPT',
314'PLN', 'EUR', 'STD', 'USD', 'QAR', 'EUR', 'RON', 'RUB', 'RWF', 'STD', 'ANG', 314'PLN', 'EUR', 'STD', 'USD', 'QAR', 'EUR', 'RON', 'RUB', 'RWF', 'STD', 'ANG',
315'MAD', 'XCD', 'SHP', 'XCD', 'XCD', 'EUR', 'XCD', 'EUR', 'USD', 'WST', 'EUR', 315'MAD', 'XCD', 'SHP', 'XCD', 'XCD', 'EUR', 'XCD', 'EUR', 'USD', 'WST', 'EUR',
316'SAR', 'SPL', 'XOF', 'RSD', 'SCR', 'SLL', 'XAG', 'SGD', 'ANG', 'ANG', 'EUR', 316'SAR', 'SPL', 'XOF', 'RSD', 'SCR', 'SLL', 'XAG', 'SGD', 'ANG', 'ANG', 'EUR',
317'EUR', 'SBD', 'SOS', 'ZAR', 'GBP', 'GBP', 'EUR', 'XDR', 'LKR', 'SDG', 'SRD', 317'EUR', 'SBD', 'SOS', 'ZAR', 'GBP', 'GBP', 'EUR', 'XDR', 'LKR', 'SDG', 'SRD',
318'NOK', 'SZL', 'SEK', 'CHF', 'SYP', 'TWD', 'RUB', 'TJS', 'TZS', 'THB', 'IDR', 318'NOK', 'SZL', 'SEK', 'CHF', 'SYP', 'TWD', 'RUB', 'TJS', 'TZS', 'THB', 'IDR',
319'TTD', 'XOF', 'NZD', 'TOP', 'TTD', 'TND', 'TRY', 'TMM', 'USD', 'TVD', 'UGX', 319'TTD', 'XOF', 'NZD', 'TOP', 'TTD', 'TND', 'TRY', 'TMM', 'USD', 'TVD', 'UGX',
320'UAH', 'AED', 'GBP', 'USD', 'USD', 'UYU', 'USD', 'UZS', 'VUV', 'EUR', 'VEB', 320'UAH', 'AED', 'GBP', 'USD', 'USD', 'UYU', 'USD', 'UZS', 'VUV', 'EUR', 'VEB',
321'VEF', 'VND', 'USD', 'USD', 'USD', 'XPF', 'MAD', 'YER', 'ZMK', 'ZWD']321'VEF', 'VND', 'USD', 'USD', 'USD', 'XPF', 'MAD', 'YER', 'ZMK', 'ZWD']
322322
323 ##updated currency this arry will contain the final result323 # #updated currency this arry will contain the final result
324 updated_currency = {}324 updated_currency = {}
325 325
326 def get_updated_currency(self, currency_array, main_currency, max_delta_days) :326 def get_updated_currency(self, currency_array, main_currency, max_delta_days) :
327 """Interface method that will retrieve the currency327 """Interface method that will retrieve the currency
328 This function has to be reinplemented in child"""328 This function has to be reinplemented in child"""
329 raise AbstractMethodError329 raise AbstractMethodError
330 330
331 def validate_cur(self, currency) :331 def validate_cur(self, currency) :
332 """Validate if the currency to update is supported"""332 """Validate if the currency to update is supported"""
333 if currency not in self.supported_currency_array :333 if currency not in self.supported_currency_array :
334 raise UnsuportedCurrencyError(currency) 334 raise UnsuportedCurrencyError(currency)
335 335
336 def get_url(self, url):336 def get_url(self, url):
337 """Return a string of a get url query"""337 """Return a string of a get url query"""
338 try:338 try:
@@ -342,42 +342,43 @@
342 objfile.close()342 objfile.close()
343 return rawfile343 return rawfile
344 except ImportError:344 except ImportError:
345 raise osv.except_osv('Error !', self.MOD_NAME+'Unable to import urllib !')345 raise osv.except_osv('Error !', self.MOD_NAME + 'Unable to import urllib !')
346 except IOError:346 except IOError:
347 raise osv.except_osv('Error !', self.MOD_NAME+'Web Service does not exist !')347 raise osv.except_osv('Error !', self.MOD_NAME + 'Web Service does not exist !')
348348
349 def check_rate_date(self, rate_date, max_delta_days):349 def check_rate_date(self, rate_date, max_delta_days):
350 """Check date constrains. WARN : rate_date must be of datetime type"""350 """Check date constrains. WARN : rate_date must be of datetime type"""
351 days_delta = (datetime.today() - rate_date).days351 days_delta = (datetime.today() - rate_date).days
352 if days_delta > max_delta_days:352 if days_delta > max_delta_days:
353 raise Exception('The rate date from ECB (%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))353 raise Exception('The rate date from (%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))
354 # We always have a warning when rate_date <> today354 # We always have a warning when rate_date <> today
355 rate_date_str = datetime.strftime(rate_date, '%Y-%m-%d')355 rate_date_str = datetime.strftime(rate_date, '%Y-%m-%d')
356 if rate_date_str != datetime.strftime(datetime.today(), '%Y-%m-%d'):356 if rate_date_str != datetime.strftime(datetime.today(), '%Y-%m-%d'):
357 self.log_info = "WARNING : the rate date from ECB (%s) is not today's date" % rate_date_str357 self.log_info = "WARNING : the rate date from ECB (%s) is not today's date" % rate_date_str
358 netsvc.Logger().notifyChannel("rate_update", netsvc.LOG_WARNING, "the rate date from ECB (%s) is not today's date" % rate_date_str)358 logger = logging.getLogger(__name__)
359359 logger.warning("the rate date from ECB (%s) is not today's date" % rate_date_str)
360360
361#Yahoo ################################################################################### 361
362#Yahoo ###################################################################################
362class Yahoo_getter(Curreny_getter_interface) :363class Yahoo_getter(Curreny_getter_interface) :
363 """Implementation of Currency_getter_factory interface364 """Implementation of Currency_getter_factory interface
364 for Yahoo finance service"""365 for Yahoo finance service"""
365 366
366 def get_updated_currency(self, currency_array, main_currency, max_delta_days):367 def get_updated_currency(self, currency_array, main_currency, max_delta_days):
367 """implementation of abstract method of Curreny_getter_interface"""368 """implementation of abstract method of Curreny_getter_interface"""
368 self.validate_cur(main_currency)369 self.validate_cur(main_currency)
369 url='http://download.finance.yahoo.com/d/quotes.txt?s="%s"=X&f=sl1c1abg'370 url = 'http://download.finance.yahoo.com/d/quotes.txt?s="%s"=X&f=sl1c1abg'
370 if main_currency in currency_array :371 if main_currency in currency_array :
371 currency_array.remove(main_currency)372 currency_array.remove(main_currency)
372 for curr in currency_array :373 for curr in currency_array :
373 self.validate_cur(curr)374 self.validate_cur(curr)
374 res = self.get_url(url%(main_currency+curr))375 res = self.get_url(url % (main_currency + curr))
375 val = res.split(',')[1]376 val = res.split(',')[1]
376 if val :377 if val :
377 self.updated_currency[curr] = val378 self.updated_currency[curr] = val
378 else :379 else :
379 raise Exception('Could not update the %s'%(curr))380 raise Exception('Could not update the %s' % (curr))
380 381
381 return self.updated_currency, self.log_info # empty string added by polish changes382 return self.updated_currency, self.log_info # empty string added by polish changes
382##Admin CH ############################################################################383##Admin CH ############################################################################
383class Admin_ch_getter(Curreny_getter_interface) :384class Admin_ch_getter(Curreny_getter_interface) :
@@ -388,35 +389,35 @@
388 """ Parse a dom node to retrieve-389 """ Parse a dom node to retrieve-
389 currencies data"""390 currencies data"""
390 res = {}391 res = {}
391 xpath_rate_currency = "/def:wechselkurse/def:devise[@code='%s']/def:kurs/text()"%(curr.lower())392 xpath_rate_currency = "/def:wechselkurse/def:devise[@code='%s']/def:kurs/text()" % (curr.lower())
392 xpath_rate_ref = "/def:wechselkurse/def:devise[@code='%s']/def:waehrung/text()"%(curr.lower())393 xpath_rate_ref = "/def:wechselkurse/def:devise[@code='%s']/def:waehrung/text()" % (curr.lower())
393 res['rate_currency'] = float(dom.xpath(xpath_rate_currency, namespaces=ns)[0])394 res['rate_currency'] = float(dom.xpath(xpath_rate_currency, namespaces=ns)[0])
394 res['rate_ref'] = float((dom.xpath(xpath_rate_ref, namespaces=ns)[0]).split(' ')[0])395 res['rate_ref'] = float((dom.xpath(xpath_rate_ref, namespaces=ns)[0]).split(' ')[0])
395 return res396 return res
396397
397 def get_updated_currency(self, currency_array, main_currency, max_delta_days):398 def get_updated_currency(self, currency_array, main_currency, max_delta_days):
398 """implementation of abstract method of Curreny_getter_interface"""399 """implementation of abstract method of Curreny_getter_interface"""
399 url='http://www.afd.admin.ch/publicdb/newdb/mwst_kurse/wechselkurse.php'400 url = 'http://www.afd.admin.ch/publicdb/newdb/mwst_kurse/wechselkurse.php'
400 #we do not want to update the main currency401 # we do not want to update the main currency
401 if main_currency in currency_array :402 if main_currency in currency_array :
402 currency_array.remove(main_currency)403 currency_array.remove(main_currency)
403 # Move to new XML lib cf Launchpad bug #645263404 # Move to new XML lib cf Launchpad bug #645263
404 from lxml import etree405 from lxml import etree
405 logger = netsvc.Logger()406 logger = logging.getLogger(__name__)
406 logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "Admin.ch currency rate service : connecting...")407 logger.debug("Admin.ch currency rate service : connecting...")
407 rawfile = self.get_url(url)408 rawfile = self.get_url(url)
408 dom = etree.fromstring(rawfile)409 dom = etree.fromstring(rawfile)
409 logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "Admin.ch sent a valid XML file")410 logger.debug("Admin.ch sent a valid XML file")
410 adminch_ns = {'def': 'http://www.afd.admin.ch/publicdb/newdb/mwst_kurse'}411 adminch_ns = {'def': 'http://www.afd.admin.ch/publicdb/newdb/mwst_kurse'}
411 rate_date = dom.xpath('/def:wechselkurse/def:datum/text()', namespaces=adminch_ns)[0]412 rate_date = dom.xpath('/def:wechselkurse/def:datum/text()', namespaces=adminch_ns)[0]
412 rate_date_datetime = datetime.strptime(rate_date, '%Y-%m-%d')413 rate_date_datetime = datetime.strptime(rate_date, '%Y-%m-%d')
413 self.check_rate_date(rate_date_datetime, max_delta_days)414 self.check_rate_date(rate_date_datetime, max_delta_days)
414 #we dynamically update supported currencies415 # we dynamically update supported currencies
415 self.supported_currency_array = dom.xpath("/def:wechselkurse/def:devise/@code", namespaces=adminch_ns)416 self.supported_currency_array = dom.xpath("/def:wechselkurse/def:devise/@code", namespaces=adminch_ns)
416 self.supported_currency_array = [x.upper() for x in self.supported_currency_array]417 self.supported_currency_array = [x.upper() for x in self.supported_currency_array]
417 self.supported_currency_array.append('CHF')418 self.supported_currency_array.append('CHF')
418419
419 logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "Supported currencies = " + str(self.supported_currency_array))420 logger.debug("Supported currencies = %s" % str(self.supported_currency_array))
420 self.validate_cur(main_currency)421 self.validate_cur(main_currency)
421 if main_currency != 'CHF':422 if main_currency != 'CHF':
422 main_curr_data = self.rate_retrieve(dom, adminch_ns, main_currency)423 main_curr_data = self.rate_retrieve(dom, adminch_ns, main_currency)
@@ -434,7 +435,7 @@
434 else :435 else :
435 rate = main_rate * curr_data['rate_ref'] / curr_data['rate_currency']436 rate = main_rate * curr_data['rate_ref'] / curr_data['rate_currency']
436 self.updated_currency[curr] = rate437 self.updated_currency[curr] = rate
437 logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "Rate retrieved : 1 " + main_currency + ' = ' + str(rate) + ' ' + curr)438 logger.debug("Rate retrieved : 1 " + main_currency + ' = ' + str(rate) + ' ' + curr)
438 return self.updated_currency, self.log_info439 return self.updated_currency, self.log_info
439440
440## ECB getter ############################################################################441## ECB getter ############################################################################
@@ -447,36 +448,36 @@
447 """ Parse a dom node to retrieve-448 """ Parse a dom node to retrieve-
448 currencies data"""449 currencies data"""
449 res = {}450 res = {}
450 xpath_curr_rate = "/gesmes:Envelope/def:Cube/def:Cube/def:Cube[@currency='%s']/@rate"%(curr.upper())451 xpath_curr_rate = "/gesmes:Envelope/def:Cube/def:Cube/def:Cube[@currency='%s']/@rate" % (curr.upper())
451 res['rate_currency'] = float(dom.xpath(xpath_curr_rate, namespaces=ns)[0])452 res['rate_currency'] = float(dom.xpath(xpath_curr_rate, namespaces=ns)[0])
452 return res453 return res
453454
454 def get_updated_currency(self, currency_array, main_currency, max_delta_days):455 def get_updated_currency(self, currency_array, main_currency, max_delta_days):
455 """implementation of abstract method of Curreny_getter_interface"""456 """implementation of abstract method of Curreny_getter_interface"""
456 url='http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml'457 url = 'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml'
457 # Important : as explained on the ECB web site, the currencies are458 # Important : as explained on the ECB web site, the currencies are
458 # at the beginning of the afternoon ; so, until 3 p.m. Paris time459 # at the beginning of the afternoon ; so, until 3 p.m. Paris time
459 # the currency rates are the ones of trading day N-1460 # the currency rates are the ones of trading day N-1
460 # see http://www.ecb.europa.eu/stats/exchange/eurofxref/html/index.en.html461 # see http://www.ecb.europa.eu/stats/exchange/eurofxref/html/index.en.html
461462
462 #we do not want to update the main currency463 # we do not want to update the main currency
463 if main_currency in currency_array :464 if main_currency in currency_array :
464 currency_array.remove(main_currency)465 currency_array.remove(main_currency)
465 # Move to new XML lib cf Launchpad bug #645263466 # Move to new XML lib cf Launchpad bug #645263
466 from lxml import etree467 from lxml import etree
467 logger = netsvc.Logger()468 logger = logging.getLogger(__name__)
468 logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "ECB currency rate service : connecting...")469 logger.debug("ECB currency rate service : connecting...")
469 rawfile = self.get_url(url)470 rawfile = self.get_url(url)
470 dom = etree.fromstring(rawfile)471 dom = etree.fromstring(rawfile)
471 logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "ECB sent a valid XML file")472 logger.debug("ECB sent a valid XML file")
472 ecb_ns = {'gesmes': 'http://www.gesmes.org/xml/2002-08-01', 'def': 'http://www.ecb.int/vocabulary/2002-08-01/eurofxref'}473 ecb_ns = {'gesmes': 'http://www.gesmes.org/xml/2002-08-01', 'def': 'http://www.ecb.int/vocabulary/2002-08-01/eurofxref'}
473 rate_date = dom.xpath('/gesmes:Envelope/def:Cube/def:Cube/@time', namespaces=ecb_ns)[0]474 rate_date = dom.xpath('/gesmes:Envelope/def:Cube/def:Cube/@time', namespaces=ecb_ns)[0]
474 rate_date_datetime = datetime.strptime(rate_date, '%Y-%m-%d')475 rate_date_datetime = datetime.strptime(rate_date, '%Y-%m-%d')
475 self.check_rate_date(rate_date_datetime, max_delta_days)476 self.check_rate_date(rate_date_datetime, max_delta_days)
476 #we dynamically update supported currencies477 # we dynamically update supported currencies
477 self.supported_currency_array = dom.xpath("/gesmes:Envelope/def:Cube/def:Cube/def:Cube/@currency", namespaces=ecb_ns)478 self.supported_currency_array = dom.xpath("/gesmes:Envelope/def:Cube/def:Cube/def:Cube/@currency", namespaces=ecb_ns)
478 self.supported_currency_array.append('EUR')479 self.supported_currency_array.append('EUR')
479 logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "Supported currencies = " + str(self.supported_currency_array))480 logger.debug("Supported currencies = %s" % str(self.supported_currency_array))
480 self.validate_cur(main_currency)481 self.validate_cur(main_currency)
481 if main_currency != 'EUR':482 if main_currency != 'EUR':
482 main_curr_data = self.rate_retrieve(dom, ecb_ns, main_currency)483 main_curr_data = self.rate_retrieve(dom, ecb_ns, main_currency)
@@ -491,11 +492,11 @@
491 else:492 else:
492 rate = curr_data['rate_currency'] / main_curr_data['rate_currency']493 rate = curr_data['rate_currency'] / main_curr_data['rate_currency']
493 self.updated_currency[curr] = rate494 self.updated_currency[curr] = rate
494 logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "Rate retrieved : 1 " + main_currency + ' = ' + str(rate) + ' ' + curr)495 logger.debug("Rate retrieved : 1 " + main_currency + ' = ' + str(rate) + ' ' + curr)
495 return self.updated_currency, self.log_info496 return self.updated_currency, self.log_info
496497
497##PL NBP ############################################################################498##PL NBP ############################################################################
498class PL_NBP_getter(Curreny_getter_interface) : # class added according to polish needs = based on class Admin_ch_getter499class PL_NBP_getter(Curreny_getter_interface) : # class added according to polish needs = based on class Admin_ch_getter
499 """Implementation of Currency_getter_factory interface500 """Implementation of Currency_getter_factory interface
500 for PL NBP service"""501 for PL NBP service"""
501502
@@ -503,39 +504,33 @@
503 """ Parse a dom node to retrieve504 """ Parse a dom node to retrieve
504 currencies data"""505 currencies data"""
505 res = {}506 res = {}
506 xpath_rate_currency = "/tabela_kursow/pozycja[kod_waluty='%s']/kurs_sredni/text()"%(curr.upper())507 xpath_rate_currency = "/tabela_kursow/pozycja[kod_waluty='%s']/kurs_sredni/text()" % (curr.upper())
507 xpath_rate_ref = "/tabela_kursow/pozycja[kod_waluty='%s']/przelicznik/text()"%(curr.upper())508 xpath_rate_ref = "/tabela_kursow/pozycja[kod_waluty='%s']/przelicznik/text()" % (curr.upper())
508 res['rate_currency'] = float(dom.xpath(xpath_rate_currency, namespaces=ns)[0].replace(',','.'))509 res['rate_currency'] = float(dom.xpath(xpath_rate_currency, namespaces=ns)[0].replace(',', '.'))
509 res['rate_ref'] = float(dom.xpath(xpath_rate_ref, namespaces=ns)[0])510 res['rate_ref'] = float(dom.xpath(xpath_rate_ref, namespaces=ns)[0])
510 return res511 return res
511512
512 def get_updated_currency(self, currency_array, main_currency, max_delta_days):513 def get_updated_currency(self, currency_array, main_currency, max_delta_days):
513 """implementation of abstract method of Curreny_getter_interface"""514 """implementation of abstract method of Curreny_getter_interface"""
514 url='http://www.nbp.pl/kursy/xml/LastA.xml' # LastA.xml is always the most recent one515 url = 'http://www.nbp.pl/kursy/xml/LastA.xml' # LastA.xml is always the most recent one
515 #we do not want to update the main currency516 # we do not want to update the main currency
516 if main_currency in currency_array :517 if main_currency in currency_array :
517 currency_array.remove(main_currency)518 currency_array.remove(main_currency)
518 # Move to new XML lib cf Launchpad bug #645263519 # Move to new XML lib cf Launchpad bug #645263
519 from lxml import etree520 from lxml import etree
520 logger = netsvc.Logger()521 logger = logging.getLogger(__name__)
521 logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "NBP.pl currency rate service : connecting...")522 logger.debug("NBP.pl currency rate service : connecting...")
522 rawfile = self.get_url(url)523 rawfile = self.get_url(url)
523 dom = etree.fromstring(rawfile) # If rawfile is not XML, it crashes here524 dom = etree.fromstring(rawfile) # If rawfile is not XML, it crashes here
524 ns = {} # Cool, there are no namespaces !525 ns = {} # Cool, there are no namespaces !
525 logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "NBP.pl sent a valid XML file")526 logger.debug("NBP.pl sent a valid XML file")
526 #node = xpath.Evaluate("/tabela_kursow", dom) # BEGIN Polish - rates table name
527 #if isinstance(node, list) :
528 # node = node[0]
529 #self.log_info = node.getElementsByTagName('numer_tabeli')[0].childNodes[0].data
530 #self.log_info = self.log_info + " " + node.getElementsByTagName('data_publikacji')[0].childNodes[0].data # END Polish - rates table name
531
532 rate_date = dom.xpath('/tabela_kursow/data_publikacji/text()', namespaces=ns)[0]527 rate_date = dom.xpath('/tabela_kursow/data_publikacji/text()', namespaces=ns)[0]
533 rate_date_datetime = datetime.strptime(rate_date, '%Y-%m-%d')528 rate_date_datetime = datetime.strptime(rate_date, '%Y-%m-%d')
534 self.check_rate_date(rate_date_datetime, max_delta_days)529 self.check_rate_date(rate_date_datetime, max_delta_days)
535 #we dynamically update supported currencies530 # we dynamically update supported currencies
536 self.supported_currency_array = dom.xpath('/tabela_kursow/pozycja/kod_waluty/text()', namespaces=ns)531 self.supported_currency_array = dom.xpath('/tabela_kursow/pozycja/kod_waluty/text()', namespaces=ns)
537 self.supported_currency_array.append('PLN')532 self.supported_currency_array.append('PLN')
538 logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "Supported currencies = " + str(self.supported_currency_array))533 logger.debug("Supported currencies = %s" % str(self.supported_currency_array))
539 self.validate_cur(main_currency)534 self.validate_cur(main_currency)
540 if main_currency != 'PLN':535 if main_currency != 'PLN':
541 main_curr_data = self.rate_retrieve(dom, ns, main_currency)536 main_curr_data = self.rate_retrieve(dom, ns, main_currency)
@@ -553,5 +548,59 @@
553 else:548 else:
554 rate = main_rate * curr_data['rate_ref'] / curr_data['rate_currency']549 rate = main_rate * curr_data['rate_ref'] / curr_data['rate_currency']
555 self.updated_currency[curr] = rate550 self.updated_currency[curr] = rate
556 logger.notifyChannel("rate_update", netsvc.LOG_DEBUG, "Rate retrieved : 1 " + main_currency + ' = ' + str(rate) + ' ' + curr)551 logger.debug("Rate retrieved : 1 " + main_currency + ' = ' + str(rate) + ' ' + curr)
552 return self.updated_currency, self.log_info
553
554##Banco de México ############################################################################
555class Banxico_getter(Curreny_getter_interface) : # class added for Mexico rates
556 """Implementation of Currency_getter_factory interface
557 for Banco de México service"""
558
559 def rate_retrieve(self):
560 """ Get currency exchange from Banxico.xml and proccess it
561 TODO: Get correct data from xml instead of process string
562 """
563 url = 'http://www.banxico.org.mx/rsscb/rss?BMXC_canal=pagos&BMXC_idioma=es'
564
565 from xml.dom.minidom import parse
566 from StringIO import StringIO
567
568 logger = logging.getLogger(__name__)
569 logger.debug("Banxico currency rate service : connecting...")
570 rawfile = self.get_url(url)
571
572 dom = parse(StringIO(rawfile))
573 logger.debug("Banxico sent a valid XML file")
574
575 value = dom.getElementsByTagName('cb:value')[0]
576 rate = value.firstChild.nodeValue
577
578 return float(rate)
579
580
581 def get_updated_currency(self, currency_array, main_currency, max_delta_days=1):
582 """implementation of abstract method of Curreny_getter_interface"""
583 logger = logging.getLogger(__name__)
584 # we do not want to update the main currency
585 if main_currency in currency_array :
586 currency_array.remove(main_currency)
587
588 # Suported currencies
589 suported = ['MXN', 'USD']
590 for curr in currency_array :
591 if curr in suported:
592 # Get currency data
593 main_rate = self.rate_retrieve()
594 if main_currency == 'MXN':
595 rate = 1 / main_rate
596 else:
597 rate = main_rate
598 else:
599 """ No other currency supported
600 """
601 continue
602
603 self.updated_currency[curr] = rate
604 logger.debug("Rate retrieved : " + main_currency + ' = ' + str(rate) + ' ' + curr)
605
557 return self.updated_currency, self.log_info606 return self.updated_currency, self.log_info
558607
=== modified file 'currency_rate_update/currency_rate_update.xml' (properties changed: +x to -x)
--- currency_rate_update/currency_rate_update.xml 2011-08-12 12:35:23 +0000
+++ currency_rate_update/currency_rate_update.xml 2013-09-17 18:13:12 +0000
@@ -9,7 +9,7 @@
9 <tree string="Rates">9 <tree string="Rates">
10 <field name="service"/>10 <field name="service"/>
11 <field name="currency_to_update"/>11 <field name="currency_to_update"/>
12 <field name="company_id"/>12 <field name="company_id" groups="base.group_multi_company"/>
1313
14 </tree>14 </tree>
15 </field>15 </field>
@@ -22,7 +22,7 @@
22 <field name="arch" type="xml">22 <field name="arch" type="xml">
23 <form string="Rate">23 <form string="Rate">
24 <field name="service"/>24 <field name="service"/>
25 <field name="company_id"/>25 <field name="company_id" groups="base.group_multi_company"/>
26 <field name="max_delta_days"/>26 <field name="max_delta_days"/>
27 <separator string="Currencies to update with this service" colspan="4"/>27 <separator string="Currencies to update with this service" colspan="4"/>
28 <field name="currency_to_update" colspan="4" nolabel="1"/>28 <field name="currency_to_update" colspan="4" nolabel="1"/>
@@ -33,4 +33,4 @@
33 </record>33 </record>
34 34
35 </data>35 </data>
36</openerp>36</openerp>
37\ No newline at end of file37\ No newline at end of file
3838
=== modified file 'currency_rate_update/security/security.xml' (properties changed: +x to -x)

Subscribers

People subscribed via source and target branches