diff --git a/addons/auth_oauth/data/auth_oauth_data.xml b/addons/auth_oauth/data/auth_oauth_data.xml
index 1b1ff532ae4..8417c9c4b2d 100644
--- a/addons/auth_oauth/data/auth_oauth_data.xml
+++ b/addons/auth_oauth/data/auth_oauth_data.xml
@@ -16,7 +16,7 @@
https://www.facebook.com/dialog/oauth
public_profile,email
https://graph.facebook.com/me
-
+ https://graph.facebook.com/me?fields=id,name,email
fa fa-facebook-square
Log in with facebook
diff --git a/addons/base_sparse_field/models/fields.py b/addons/base_sparse_field/models/fields.py
index cb1e1981193..e2b8adc0e29 100644
--- a/addons/base_sparse_field/models/fields.py
+++ b/addons/base_sparse_field/models/fields.py
@@ -81,7 +81,7 @@ class Serialized(fields.Field):
}
column_type = ('text', 'text')
- def convert_to_column(self, value, record):
+ def convert_to_column(self, value, record, values=None):
return json.dumps(value)
def convert_to_cache(self, value, record, validate=True):
diff --git a/addons/point_of_sale/static/src/js/db.js b/addons/point_of_sale/static/src/js/db.js
index b70b3517bf2..f777dc27324 100644
--- a/addons/point_of_sale/static/src/js/db.js
+++ b/addons/point_of_sale/static/src/js/db.js
@@ -301,7 +301,7 @@ var PosDB = core.Class.extend({
search_partner: function(query){
try {
query = query.replace(/[\[\]\(\)\+\*\?\.\-\!\&\^\$\|\~\_\{\}\:\,\\\/]/g,'.');
- query = query.replace(' ','.+');
+ query = query.replace(/ /g,'.+');
var re = RegExp("([0-9]+):.*?"+query,"gi");
}catch(e){
return [];
diff --git a/addons/sales_team/models/crm_team.py b/addons/sales_team/models/crm_team.py
index 401c6d6c8cb..02dcc93554d 100644
--- a/addons/sales_team/models/crm_team.py
+++ b/addons/sales_team/models/crm_team.py
@@ -24,7 +24,7 @@ class CrmTeam(models.Model):
def _get_default_team_id(self, user_id=None):
if not user_id:
user_id = self.env.uid
- company_id = self.sudo(user_id).company_id.id
+ company_id = self.sudo(user_id).env.user.company_id.id
team_id = self.env['crm.team'].sudo().search([
'|', ('user_id', '=', user_id), ('member_ids', '=', user_id),
'|', ('company_id', '=', False), ('company_id', 'child_of', [company_id])
diff --git a/addons/web/static/src/js/views/search_view.js b/addons/web/static/src/js/views/search_view.js
index 65dbb4557e7..d11af68c107 100644
--- a/addons/web/static/src/js/views/search_view.js
+++ b/addons/web/static/src/js/views/search_view.js
@@ -337,7 +337,7 @@ var SearchView = View.extend({
},
set_default_filters: function () {
var self = this,
- default_custom_filter = this.$buttons && this.favorite_menu.get_default_filter();
+ default_custom_filter = this.$buttons && this.favorite_menu && this.favorite_menu.get_default_filter();
if (!self.options.disable_custom_filters && default_custom_filter) {
return this.favorite_menu.toggle_filter(default_custom_filter, true);
}
diff --git a/doc/cla/individual/ruter.md b/doc/cla/individual/ruter.md
new file mode 100644
index 00000000000..1560ba2b7f3
--- /dev/null
+++ b/doc/cla/individual/ruter.md
@@ -0,0 +1,11 @@
+China, 2017-12-05
+
+I hereby agree to the terms of the Odoo Individual Contributor License
+Agreement v1.0.
+
+I declare that I am authorized and able to make this agreement and sign this
+declaration.
+
+Signed,
+
+Ruter ruter.lv@gmail.com https://github.com/ruter
diff --git a/odoo/addons/test_new_api/tests/test_new_fields.py b/odoo/addons/test_new_api/tests/test_new_fields.py
index 94bc0e23c55..a6af588468a 100644
--- a/odoo/addons/test_new_api/tests/test_new_fields.py
+++ b/odoo/addons/test_new_api/tests/test_new_fields.py
@@ -5,7 +5,7 @@ from datetime import date, datetime
from odoo.exceptions import AccessError, except_orm
from odoo.tests import common
-from odoo.tools import mute_logger
+from odoo.tools import mute_logger, float_repr
class TestFields(common.TransactionCase):
@@ -305,21 +305,65 @@ class TestFields(common.TransactionCase):
record.number = 2.4999999999999996
self.assertEqual(record.number, 2.50)
+ def check_monetary(self, record, amount, currency, msg=None):
+ # determine the possible roundings of amount
+ if currency:
+ ramount = currency.round(amount)
+ samount = float(float_repr(ramount, currency.decimal_places))
+ else:
+ ramount = samount = amount
+
+ # check the currency on record
+ self.assertEqual(record.currency_id, currency)
+
+ # check the value on the record
+ self.assertIn(record.amount, [ramount, samount], msg)
+
+ # check the value in the database
+ self.cr.execute('SELECT amount FROM test_new_api_mixed WHERE id=%s', [record.id])
+ value = self.cr.fetchone()[0]
+ self.assertEqual(value, samount, msg)
+
def test_20_monetary(self):
""" test monetary fields """
- record = self.env['test_new_api.mixed'].create({})
- self.assertTrue(record.currency_id)
- self.assertEqual(record.currency_id.rounding, 0.01)
+ model = self.env['test_new_api.mixed']
+ currency = self.env['res.currency'].with_context(active_test=False)
+ amount = 14.70126
- # the conversion to cache should round the value to 14.700000000000001
- record.amount = 14.7
- self.assertNotEqual(record.amount, 14.7)
- self.assertEqual(record.amount, 14.700000000000001)
+ for rounding in [0.01, 0.0001, 1.0, 0]:
+ # first retrieve a currency corresponding to rounding
+ if rounding:
+ currency = currency.search([('rounding', '=', rounding)], limit=1)
+ self.assertTrue(currency, "No currency found for rounding %s" % rounding)
+ else:
+ # rounding=0 corresponds to currency=False
+ currency = currency.browse()
- # however when stored to database, it should be serialized as 14.70
- self.cr.execute('SELECT amount FROM test_new_api_mixed WHERE id=%s', (record.id,))
- (amount,) = self.cr.fetchone()
- self.assertEqual(amount, 14.7)
+ # case 1: create with amount and currency
+ record = model.create({'amount': amount, 'currency_id': currency.id})
+ self.check_monetary(record, amount, currency, 'create(amount, currency)')
+
+ # case 2: assign amount
+ record.amount = 0
+ record.amount = amount
+ self.check_monetary(record, amount, currency, 'assign(amount)')
+
+ # case 3: write with amount and currency
+ record.write({'amount': 0, 'currency_id': False})
+ record.write({'amount': amount, 'currency_id': currency.id})
+ self.check_monetary(record, amount, currency, 'write(amount, currency)')
+
+ # case 4: write with amount only
+ record.write({'amount': 0})
+ record.write({'amount': amount})
+ self.check_monetary(record, amount, currency, 'write(amount)')
+
+ # case 5: write with amount on several records
+ records = record + model.create({'currency_id': currency.id})
+ records.write({'amount': 0})
+ records.write({'amount': amount})
+ for record in records:
+ self.check_monetary(record, amount, currency, 'multi write(amount)')
def test_21_date(self):
""" test date fields """
diff --git a/odoo/fields.py b/odoo/fields.py
index 608adcfd045..3f32f68a765 100644
--- a/odoo/fields.py
+++ b/odoo/fields.py
@@ -15,7 +15,7 @@ import xmlrpclib
import psycopg2
from odoo.sql_db import LazyCursor
-from odoo.tools import float_precision, float_repr, float_round, frozendict, \
+from odoo.tools import float_repr, float_round, frozendict, \
html_sanitize, human_size, pg_varchar, ustr, OrderedSet
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT as DATE_FORMAT
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT as DATETIME_FORMAT
@@ -773,7 +773,7 @@ class Field(object):
""" Return the null value for this field in the record format. """
return False
- def convert_to_column(self, value, record):
+ def convert_to_column(self, value, record, values=None):
""" Convert ``value`` from the ``write`` format to the SQL format. """
if value is None or value == False:
return None
@@ -1177,7 +1177,7 @@ class Boolean(Field):
type = 'boolean'
column_type = ('bool', 'bool')
- def convert_to_column(self, value, record):
+ def convert_to_column(self, value, record, values=None):
return bool(value)
def convert_to_cache(self, value, record, validate=True):
@@ -1198,7 +1198,7 @@ class Integer(Field):
_description_group_operator = property(attrgetter('group_operator'))
- def convert_to_column(self, value, record):
+ def convert_to_column(self, value, record, values=None):
return int(value or 0)
def convert_to_cache(self, value, record, validate=True):
@@ -1262,7 +1262,7 @@ class Float(Field):
_description_digits = property(attrgetter('digits'))
_description_group_operator = property(attrgetter('group_operator'))
- def convert_to_column(self, value, record):
+ def convert_to_column(self, value, record, values=None):
result = float(value or 0.0)
digits = self.digits
if digits:
@@ -1316,25 +1316,34 @@ class Monetary(Field):
assert self.currency_field in model._fields, \
"Field %s with unknown currency_field %r" % (self, self.currency_field)
- def convert_to_column(self, value, record):
- try:
- return value.float_repr() # see float_precision.float_repr()
- except Exception:
- return float(value or 0.0)
+ def convert_to_column(self, value, record, values=None):
+ # retrieve currency from values or record
+ if values and self.currency_field in values:
+ field = record._fields[self.currency_field]
+ currency = field.convert_to_cache(values[self.currency_field], record)
+ currency = field.convert_to_record(currency, record)
+ else:
+ # Note: this is wrong if 'record' is several records with different
+ # currencies, which is functional nonsense and should not happen
+ currency = record[:1][self.currency_field]
+
+ value = float(value or 0.0)
+ if currency:
+ return float_repr(currency.round(value), currency.decimal_places)
+ return value
def convert_to_cache(self, value, record, validate=True):
- if validate:
- currency = record[self.currency_field]
+ # cache format: float
+ value = float(value or 0.0)
+ if validate and record[self.currency_field]:
# FIXME @rco-odoo: currency may not be already initialized if it is
# a function or related field!
- if currency:
- value = currency.round(float(value or 0.0))
- return float_precision(value, currency.decimal_places)
- return float(value or 0.0)
+ value = record[self.currency_field].round(value)
+ return value
def convert_to_read(self, value, record, use_name_get=True):
# float_precision values are not supported in pure XMLRPC
- return float(value)
+ return value
def convert_to_write(self, value, record):
return value
@@ -1435,7 +1444,7 @@ class Char(_String):
assert isinstance(self.size, (NoneType, int)), \
"Char field %s with non-integer size %r" % (self, self.size)
- def convert_to_column(self, value, record):
+ def convert_to_column(self, value, record, values=None):
#TODO:
# * we need to remove the "value==False" from the next line BUT
# for now too many things rely on this broken behavior
@@ -1504,7 +1513,7 @@ class Html(_String):
_description_strip_style = property(attrgetter('strip_style'))
_description_strip_classes = property(attrgetter('strip_classes'))
- def convert_to_column(self, value, record):
+ def convert_to_column(self, value, record, values=None):
if value is None or value is False:
return None
if self.sanitize:
@@ -1685,7 +1694,7 @@ class Binary(Field):
_description_attachment = property(attrgetter('attachment'))
- def convert_to_column(self, value, record):
+ def convert_to_column(self, value, record, values=None):
# Binary values may be byte strings (python 2.6 byte array), but
# the legacy OpenERP convention is to transfer and store binaries
# as base64-encoded strings. The base64 string may be provided as a
@@ -1845,7 +1854,7 @@ class Selection(Field):
return item[1]
return False
- def convert_to_column(self, value, record):
+ def convert_to_column(self, value, record, values=None):
""" Convert ``value`` from the ``write`` format to the SQL format. """
if value is None or value is False:
return None
@@ -2012,7 +2021,7 @@ class Many2one(_Relational):
"""
records._cache[self] = self.convert_to_cache(value, records, validate=False)
- def convert_to_column(self, value, record):
+ def convert_to_column(self, value, record, values=None):
return value or None
def convert_to_cache(self, value, record, validate=True):
diff --git a/odoo/models.py b/odoo/models.py
index 8d049eae7e3..b3aec1441cc 100644
--- a/odoo/models.py
+++ b/odoo/models.py
@@ -3072,7 +3072,7 @@ class BaseModel(object):
if field.column_type:
if single_lang or not (has_trans and field.translate is True):
# val is not a translation: update the table
- val = field.convert_to_column(val, self)
+ val = field.convert_to_column(val, self, vals)
updates.append((name, field.column_format, val))
direct.append(name)
else:
@@ -3112,7 +3112,7 @@ class BaseModel(object):
# Insert value to DB
src_trans = vals[name]
self.with_context(lang=None).write({name: src_trans})
- val = field.convert_to_column(vals[name], self)
+ val = field.convert_to_column(vals[name], self, vals)
tname = "%s,%s" % (self._name, name)
self.env['ir.translation']._set_ids(
tname, 'model', self.env.lang, self.ids, val, src_trans)
@@ -3336,7 +3336,7 @@ class BaseModel(object):
parent_id = self.env[parent_model].create(parent_vals).id
else:
self.env[parent_model].browse(parent_id).write(parent_vals)
- updates.append((self._inherits[parent_model], '%s', parent_id))
+ vals[self._inherits[parent_model]] = parent_id
# set boolean fields to False by default (to make search more powerful)
for name, field in self._fields.iteritems():
@@ -3344,10 +3344,12 @@ class BaseModel(object):
vals[name] = False
# determine SQL values
+ self = self.browse()
for name, val in vals.iteritems():
field = self._fields[name]
if field.store and field.column_type:
- updates.append((name, field.column_format, field.convert_to_column(val, self)))
+ column_val = field.convert_to_column(val, self, vals)
+ updates.append((name, field.column_format, column_val))
else:
upd_todo.append(name)
diff --git a/odoo/tools/float_utils.py b/odoo/tools/float_utils.py
index 127c546125f..69c11148abe 100644
--- a/odoo/tools/float_utils.py
+++ b/odoo/tools/float_utils.py
@@ -176,24 +176,6 @@ def float_split(value, precision_digits):
return int(units), int(cents)
-class float_precision(float):
- """ A class for float values that carry precision digits. This is a thin
- layer on top of ``float``, and the precision digits are not propagated
- to the result of arithmetic operations. This class is used when
- converting monetary values to cache, and for serializing them to the
- database.
- """
- __slots__ = ['precision_digits']
-
- def __new__(cls, value, precision_digits):
- obj = super(float_precision, cls).__new__(cls, value)
- obj.precision_digits = precision_digits
- return obj
-
- def float_repr(self):
- return _float_repr(self, self.precision_digits)
-
-
if __name__ == "__main__":
import time