From 48daabd4659bb54d3a51b78064865e90d44914d6 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Wed, 31 Jul 2013 12:06:26 +0200 Subject: [PATCH 1/4] [FIX] res_currency: avoid getting blocked when creating a curreny without rates bzr revid: mat@openerp.com-20130731100626-kgxpvfkwkg13lpy1 --- openerp/addons/base/res/res_currency.py | 31 +++++++++++++++++++ openerp/addons/base/res/res_currency_view.xml | 4 +-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/openerp/addons/base/res/res_currency.py b/openerp/addons/base/res/res_currency.py index 4756cdbe01a..fa81c469520 100644 --- a/openerp/addons/base/res/res_currency.py +++ b/openerp/addons/base/res/res_currency.py @@ -51,6 +51,33 @@ class res_currency(osv.osv): else: raise osv.except_osv(_('Error!'),_("No currency rate associated for currency %d for the given period" % (id))) return res + + def _current_rate_silent(self, cr, uid, ids, name, arg, context=None): + """Same function as currency_rate with 0 for undefined rates + Not using a wrapper over _current_rate as it would produce only 0 in a + tree view with one error + """ + if context is None: + context = {} + res = {} + if 'date' in context: + date = context['date'] + else: + date = time.strftime('%Y-%m-%d') + date = date or time.strftime('%Y-%m-%d') + # Convert False values to None ... + currency_rate_type = context.get('currency_rate_type_id') or None + # ... and use 'is NULL' instead of '= some-id'. + operator = '=' if currency_rate_type else 'is' + for id in ids: + cr.execute("SELECT currency_id, rate FROM res_currency_rate WHERE currency_id = %s AND name <= %s AND currency_rate_type_id " + operator +" %s ORDER BY name desc LIMIT 1" ,(id, date, currency_rate_type)) + if cr.rowcount: + id, rate = cr.fetchall()[0] + res[id] = rate + else: + res[id] = 0 + return res + _name = "res.currency" _description = "Currency" _columns = { @@ -59,6 +86,10 @@ class res_currency(osv.osv): 'symbol': fields.char('Symbol', size=4, help="Currency sign, to be used when printing amounts."), 'rate': fields.function(_current_rate, string='Current Rate', digits=(12,6), help='The rate of the currency to the currency of rate 1.'), + + # Do not use for computation ! Same as rate field with silent failing + 'rate_silent': fields.function(_current_rate_silent, string='Current Rate', digits=(12,6), + help='The rate of the currency to the currency of rate 1 (0 if no rate defined).'), 'rate_ids': fields.one2many('res.currency.rate', 'currency_id', 'Rates'), 'accuracy': fields.integer('Computational Accuracy'), 'rounding': fields.float('Rounding Factor', digits=(12,6)), diff --git a/openerp/addons/base/res/res_currency_view.xml b/openerp/addons/base/res/res_currency_view.xml index 907eec866fb..c74b677ea72 100644 --- a/openerp/addons/base/res/res_currency_view.xml +++ b/openerp/addons/base/res/res_currency_view.xml @@ -22,7 +22,7 @@ - + @@ -37,7 +37,7 @@
- + From fc2eb46b40ba749289c7ef001594d0785a58dc1c Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Wed, 31 Jul 2013 13:05:25 +0200 Subject: [PATCH 2/4] [IMP] res_currency: more modular solution bzr revid: mat@openerp.com-20130731110525-garlkomohzcyp4pd --- openerp/addons/base/res/res_currency.py | 32 ++++++------------------- 1 file changed, 7 insertions(+), 25 deletions(-) diff --git a/openerp/addons/base/res/res_currency.py b/openerp/addons/base/res/res_currency.py index fa81c469520..d986c1885b2 100644 --- a/openerp/addons/base/res/res_currency.py +++ b/openerp/addons/base/res/res_currency.py @@ -31,32 +31,12 @@ CURRENCY_DISPLAY_PATTERN = re.compile(r'(\w+)\s*(?:\((.*)\))?') class res_currency(osv.osv): def _current_rate(self, cr, uid, ids, name, arg, context=None): - if context is None: - context = {} - res = {} - if 'date' in context: - date = context['date'] - else: - date = time.strftime('%Y-%m-%d') - date = date or time.strftime('%Y-%m-%d') - # Convert False values to None ... - currency_rate_type = context.get('currency_rate_type_id') or None - # ... and use 'is NULL' instead of '= some-id'. - operator = '=' if currency_rate_type else 'is' - for id in ids: - cr.execute("SELECT currency_id, rate FROM res_currency_rate WHERE currency_id = %s AND name <= %s AND currency_rate_type_id " + operator +" %s ORDER BY name desc LIMIT 1" ,(id, date, currency_rate_type)) - if cr.rowcount: - id, rate = cr.fetchall()[0] - res[id] = rate - else: - raise osv.except_osv(_('Error!'),_("No currency rate associated for currency %d for the given period" % (id))) - return res + return self._current_rate_computation(cr, uid, ids, name, arg, raise_on_no_rate=True, context=None) def _current_rate_silent(self, cr, uid, ids, name, arg, context=None): - """Same function as currency_rate with 0 for undefined rates - Not using a wrapper over _current_rate as it would produce only 0 in a - tree view with one error - """ + return self._current_rate_computation(cr, uid, ids, name, arg, raise_on_no_rate=False, context=None) + + def _current_rate_computation(self, cr, uid, ids, name, arg, raise_on_no_rate, context=None): if context is None: context = {} res = {} @@ -74,8 +54,10 @@ class res_currency(osv.osv): if cr.rowcount: id, rate = cr.fetchall()[0] res[id] = rate - else: + elif not raise_on_no_rate: res[id] = 0 + else: + raise osv.except_osv(_('Error!'),_("No currency rate associated for currency %d for the given period" % (id))) return res _name = "res.currency" From 3393aa645c82a395024519a97b14572e9db18d06 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Wed, 31 Jul 2013 13:10:54 +0200 Subject: [PATCH 3/4] [FIX] res_currency: update linked tests bzr revid: mat@openerp.com-20130731111054-g5p094izprzok3eo --- openerp/tests/test_acl.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/openerp/tests/test_acl.py b/openerp/tests/test_acl.py index 1d8d6bfb22a..9f9019e5db9 100644 --- a/openerp/tests/test_acl.py +++ b/openerp/tests/test_acl.py @@ -29,18 +29,18 @@ class TestACL(common.TransactionCase): view_arch = etree.fromstring(form_view.get('arch')) has_tech_feat = self.res_users.has_group(self.cr, self.demo_uid, GROUP_TECHNICAL_FEATURES) self.assertFalse(has_tech_feat, "`demo` user should not belong to the restricted group before the test") - self.assertTrue('rate' in original_fields, "'rate' field must be properly visible before the test") - self.assertNotEquals(view_arch.xpath("//field[@name='rate']"), [], - "Field 'rate' must be found in view definition before the test") + self.assertTrue('accuracy' in original_fields, "'accuracy' field must be properly visible before the test") + self.assertNotEquals(view_arch.xpath("//field[@name='accuracy']"), [], + "Field 'accuracy' must be found in view definition before the test") # Restrict access to the field and check it's gone - self.res_currency._columns['rate'].groups = GROUP_TECHNICAL_FEATURES + self.res_currency._columns['accuracy'].groups = GROUP_TECHNICAL_FEATURES fields = self.res_currency.fields_get(self.cr, self.demo_uid, []) form_view = self.res_currency.fields_view_get(self.cr, self.demo_uid, False, 'form') view_arch = etree.fromstring(form_view.get('arch')) - self.assertFalse('rate' in fields, "'rate' field should be gone") - self.assertEquals(view_arch.xpath("//field[@name='rate']"), [], - "Field 'rate' must not be found in view definition") + self.assertFalse('accuracy' in fields, "'accuracy' field should be gone") + self.assertEquals(view_arch.xpath("//field[@name='accuracy']"), [], + "Field 'accuracy' must not be found in view definition") # Make demo user a member of the restricted group and check that the field is back self.tech_group.write({'users': [(4, self.demo_uid)]}) @@ -50,13 +50,13 @@ class TestACL(common.TransactionCase): view_arch = etree.fromstring(form_view.get('arch')) #import pprint; pprint.pprint(fields); pprint.pprint(form_view) self.assertTrue(has_tech_feat, "`demo` user should now belong to the restricted group") - self.assertTrue('rate' in fields, "'rate' field must be properly visible again") - self.assertNotEquals(view_arch.xpath("//field[@name='rate']"), [], - "Field 'rate' must be found in view definition again") + self.assertTrue('accuracy' in fields, "'accuracy' field must be properly visible again") + self.assertNotEquals(view_arch.xpath("//field[@name='accuracy']"), [], + "Field 'accuracy' must be found in view definition again") #cleanup self.tech_group.write({'users': [(3, self.demo_uid)]}) - self.res_currency._columns['rate'].groups = False + self.res_currency._columns['accuracy'].groups = False @mute_logger('openerp.osv.orm') def test_field_crud_restriction(self): From ef3e11c03d69b0067cfab7cb9265d18b2aa4b254 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Wed, 31 Jul 2013 14:38:12 +0200 Subject: [PATCH 4/4] [FIX] res_currency: pass context to currency function bzr revid: mat@openerp.com-20130731123812-oztjyktifs23lhi9 --- openerp/addons/base/res/res_currency.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openerp/addons/base/res/res_currency.py b/openerp/addons/base/res/res_currency.py index d986c1885b2..dac5988b585 100644 --- a/openerp/addons/base/res/res_currency.py +++ b/openerp/addons/base/res/res_currency.py @@ -31,10 +31,10 @@ CURRENCY_DISPLAY_PATTERN = re.compile(r'(\w+)\s*(?:\((.*)\))?') class res_currency(osv.osv): def _current_rate(self, cr, uid, ids, name, arg, context=None): - return self._current_rate_computation(cr, uid, ids, name, arg, raise_on_no_rate=True, context=None) + return self._current_rate_computation(cr, uid, ids, name, arg, True, context=context) def _current_rate_silent(self, cr, uid, ids, name, arg, context=None): - return self._current_rate_computation(cr, uid, ids, name, arg, raise_on_no_rate=False, context=None) + return self._current_rate_computation(cr, uid, ids, name, arg, False, context=context) def _current_rate_computation(self, cr, uid, ids, name, arg, raise_on_no_rate, context=None): if context is None: