[MERGE] forward port branch saas-14 up to e9c2dcd28d
This commit is contained in:
@@ -16,7 +16,7 @@
|
||||
<field name="auth_endpoint">https://www.facebook.com/dialog/oauth</field>
|
||||
<field name="scope">public_profile,email</field>
|
||||
<field name="validation_endpoint">https://graph.facebook.com/me</field>
|
||||
<field name="data_endpoint"></field>
|
||||
<field name="data_endpoint">https://graph.facebook.com/me?fields=id,name,email</field>
|
||||
<field name="css_class">fa fa-facebook-square</field>
|
||||
<field name="body">Log in with facebook</field>
|
||||
</record>
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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 [];
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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 """
|
||||
|
||||
+31
-22
@@ -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):
|
||||
|
||||
+6
-4
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user