summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Ledoux <dle@odoo.com>2016-01-29 12:29:17 +0100
committerDenis Ledoux <dle@odoo.com>2016-01-29 12:29:17 +0100
commita8421996872922d1c71ecbd38b0ebf914fbb3e5b (patch)
tree45efaa081a6ad801d129dd47bba945d8ba08ded8
parentf5fedbcb18a57ee9aeab952f3ff95f692f7a863c (diff)
parent2616defe35d93193418a97030c307ec6551d0b00 (diff)
[MERGE] forward port of branch saas-6 up to 2616def
-rw-r--r--addons/account/tests/test_tax.py2
-rw-r--r--addons/hw_escpos/controllers/main.py2
-rw-r--r--addons/mrp_repair/views/report_mrprepairorder.xml14
-rw-r--r--addons/web/static/src/js/widgets/data_export.js1
-rw-r--r--addons/website/models/website.py2
-rw-r--r--openerp/addons/test_new_api/tests/test_new_fields.py36
-rw-r--r--openerp/fields.py21
-rw-r--r--openerp/modules/module.py2
-rw-r--r--openerp/osv/fields.py15
-rw-r--r--openerp/sql_db.py34
10 files changed, 97 insertions, 32 deletions
diff --git a/addons/account/tests/test_tax.py b/addons/account/tests/test_tax.py
index 2b9e11045e9..d221e705412 100644
--- a/addons/account/tests/test_tax.py
+++ b/addons/account/tests/test_tax.py
@@ -91,7 +91,7 @@ class TestTax(AccountTestUsers):
def test_tax_currency(self):
self.division_tax.amount = 15.0
res = self.division_tax.compute_all(200.0, currency=self.env.ref('base.VEF'))
- self.assertEquals(res['total_included'], 235.2941)
+ self.assertAlmostEqual(res['total_included'], 235.2941)
def test_tax_move_lines_creation(self):
""" Test that creating a move.line with tax_ids generates the tax move lines and adjust line amount when a tax is price_include """
diff --git a/addons/hw_escpos/controllers/main.py b/addons/hw_escpos/controllers/main.py
index 7fc5aa8798b..84e77065736 100644
--- a/addons/hw_escpos/controllers/main.py
+++ b/addons/hw_escpos/controllers/main.py
@@ -195,6 +195,7 @@ class EscposDriver(Thread):
localips = ['0.0.0.0','127.0.0.1','127.0.1.1']
hosting_ap = os.system('pgrep hostapd') == 0
ssid = subprocess.check_output('iwconfig 2>&1 | grep \'ESSID:"\' | sed \'s/.*"\\(.*\\)"/\\1/\'', shell=True).rstrip()
+ mac = subprocess.check_output('ifconfig | grep -B 1 \'inet addr\' | grep -o \'HWaddr .*\' | sed \'s/HWaddr //\'', shell=True).rstrip()
ips = [ c.split(':')[1].split(' ')[0] for c in commands.getoutput("/sbin/ifconfig").split('\n') if 'inet addr' in c ]
ips = [ ip for ip in ips if ip not in localips ]
eprint.text('\n\n')
@@ -218,6 +219,7 @@ class EscposDriver(Thread):
eprint.text(ip+'\n')
if len(ips) >= 1:
+ eprint.text('\nMAC Address:\n' + mac + '\n')
eprint.text('\nHomepage:\nhttp://'+ips[0]+':8069\n')
eprint.text('\n\n')
diff --git a/addons/mrp_repair/views/report_mrprepairorder.xml b/addons/mrp_repair/views/report_mrprepairorder.xml
index c9ca68c39b3..0e66cba9ebc 100644
--- a/addons/mrp_repair/views/report_mrprepairorder.xml
+++ b/addons/mrp_repair/views/report_mrprepairorder.xml
@@ -10,13 +10,15 @@
<div class="row">
<div class="col-xs-6">
- <p t-if="o.address_id == o.partner_invoice_id">Invoice and shipping address:</p>
- <p t-if="o.address_id != o.partner_invoice_id">Invoice address: </p>
- <div t-field="o.partner_invoice_id"
- t-field-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": true, "phone_icons": true}'/>
- <p t-if="o.partner_id.vat">VAT: <span t-field="o.partner_id.vat"/></p>
+ <strong t-if="o.address_id == o.partner_invoice_id">Invoice and shipping address:</strong>
+ <div t-if="o.partner_invoice_id">
+ <strong t-if="o.address_id != o.partner_invoice_id">Invoice address: </strong>
+ <div t-field="o.partner_invoice_id"
+ t-field-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": true, "phone_icons": true}'/>
+ <p t-if="o.partner_id.vat">VAT: <span t-field="o.partner_id.vat"/></p>
+ </div>
<div t-if="o.address_id != o.partner_invoice_id">
- <p>Shipping address :</p>
+ <strong>Shipping address :</strong>
<div t-field="o.address_id"
t-field-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": true, "phone_icons": true}'/>
<p t-if="o.partner_id.vat">VAT: <span t-field="o.partner_id.vat"/></p>
diff --git a/addons/web/static/src/js/widgets/data_export.js b/addons/web/static/src/js/widgets/data_export.js
index 57159732ad6..a73020ce9b2 100644
--- a/addons/web/static/src/js/widgets/data_export.js
+++ b/addons/web/static/src/js/widgets/data_export.js
@@ -388,6 +388,7 @@ var DataExport = Dialog.extend({
},
add_field: function(field_id, string) {
var field_list = this.$el.find('#fields_list');
+ field_id = this.records[field_id] || field_id;
if (this.$el.find("#fields_list option[value='" + field_id + "']")
&& !this.$el.find("#fields_list option[value='" + field_id + "']").length) {
field_list.append(new Option(string, field_id));
diff --git a/addons/website/models/website.py b/addons/website/models/website.py
index 4aa3deb1d4c..9e3249f1400 100644
--- a/addons/website/models/website.py
+++ b/addons/website/models/website.py
@@ -468,7 +468,7 @@ class website(osv.osv):
:rtype: bool
"""
endpoint = rule.endpoint
- methods = endpoint.routing.get('method') or ['GET']
+ methods = endpoint.routing.get('methods') or ['GET']
converters = rule._converters.values()
if not ('GET' in methods
diff --git a/openerp/addons/test_new_api/tests/test_new_fields.py b/openerp/addons/test_new_api/tests/test_new_fields.py
index ba8cb0516c3..8a7d9225f3b 100644
--- a/openerp/addons/test_new_api/tests/test_new_fields.py
+++ b/openerp/addons/test_new_api/tests/test_new_fields.py
@@ -422,28 +422,46 @@ class TestNewFields(common.TransactionCase):
@mute_logger('openerp.addons.base.ir.ir_model')
def test_41_new_related(self):
- """ test the behavior of related fields on new records. """
- discussion = self.env.ref('test_new_api.discussion_0')
- access = self.env.ref('test_new_api.access_discussion')
-
+ """ test the behavior of related fields starting on new records. """
# make discussions unreadable for demo user
+ access = self.env.ref('test_new_api.access_discussion')
access.write({'perm_read': False})
# create an environment for demo user
- demo_env = self.env(user=self.env.ref('base.user_demo'))
- self.assertEqual(demo_env.user.login, "demo")
+ env = self.env(user=self.env.ref('base.user_demo'))
+ self.assertEqual(env.user.login, "demo")
# create a new message as demo user
- values = {'discussion': discussion.id}
- message = demo_env['test_new_api.message'].new(values)
+ discussion = self.env.ref('test_new_api.discussion_0')
+ message = env['test_new_api.message'].new({'discussion': discussion})
self.assertEqual(message.discussion, discussion)
# read the related field discussion_name
- self.assertEqual(message.discussion.env, demo_env)
+ self.assertEqual(message.discussion.env, env)
self.assertEqual(message.discussion_name, discussion.name)
with self.assertRaises(AccessError):
message.discussion.name
+ @mute_logger('openerp.addons.base.ir.ir_model')
+ def test_42_new_related(self):
+ """ test the behavior of related fields traversing new records. """
+ # make discussions unreadable for demo user
+ access = self.env.ref('test_new_api.access_discussion')
+ access.write({'perm_read': False})
+
+ # create an environment for demo user
+ env = self.env(user=self.env.ref('base.user_demo'))
+ self.assertEqual(env.user.login, "demo")
+
+ # create a new discussion and a new message as demo user
+ discussion = env['test_new_api.discussion'].new({'name': 'Stuff'})
+ message = env['test_new_api.message'].new({'discussion': discussion})
+ self.assertEqual(message.discussion, discussion)
+
+ # read the related field discussion_name
+ self.assertNotEqual(message.sudo().env, message.env)
+ self.assertEqual(message.discussion_name, discussion.name)
+
def test_50_defaults(self):
""" test default values. """
fields = ['discussion', 'body', 'author', 'size']
diff --git a/openerp/fields.py b/openerp/fields.py
index 7576a2a4f3c..e86e3582f59 100644
--- a/openerp/fields.py
+++ b/openerp/fields.py
@@ -12,6 +12,7 @@ import logging
import pytz
import xmlrpclib
+from openerp.sql_db import LazyCursor
from openerp.tools import float_round, frozendict, html_sanitize, ustr, OrderedSet
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT as DATE_FORMAT
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT as DATETIME_FORMAT
@@ -41,6 +42,15 @@ def _check_value(value):
""" Return ``value``, or call its getter if ``value`` is a :class:`SpecialValue`. """
return value.get() if isinstance(value, SpecialValue) else value
+def copy_cache(records, env):
+ """ Recursively copy the cache of ``records`` to the environment ``env``. """
+ for record, target in zip(records, records.with_env(env)):
+ if not target._cache:
+ for name, value in record._cache.iteritems():
+ target[name] = value
+ if isinstance(value, BaseModel):
+ copy_cache(value, env)
+
def resolve_mro(model, name, predicate):
""" Return the list of successively overridden values of attribute ``name``
@@ -560,10 +570,9 @@ class Field(object):
# when related_sudo, bypass access rights checks when reading values
others = records.sudo() if self.related_sudo else records
for record, other in zip(records, others):
- if not record.id:
+ if not record.id and record.env != other.env:
# draft records: copy record's cache to other's cache first
- for name, value in record._cache.iteritems():
- other[name] = value
+ copy_cache(record, other.env)
other, field = self.traverse_related(other)
record[self.name] = other[field.name]
@@ -1074,7 +1083,7 @@ class Float(Field):
@property
def digits(self):
if callable(self._digits):
- with fields._get_cursor() as cr:
+ with LazyCursor() as cr:
return self._digits(cr)
else:
return self._digits
@@ -1091,6 +1100,8 @@ class Float(Field):
def convert_to_cache(self, value, record, validate=True):
# apply rounding here, otherwise value in cache may be wrong!
value = float(value or 0.0)
+ if not validate:
+ return value
digits = self.digits
return float_round(value, precision_digits=digits[1]) if digits else value
@@ -1980,7 +1991,7 @@ class Id(Field):
raise TypeError("field 'id' cannot be assigned")
# imported here to avoid dependency cycle issues
-from openerp import SUPERUSER_ID, registry
+from openerp import SUPERUSER_ID
from .exceptions import Warning, AccessError, MissingError
from .models import check_pg_name, BaseModel, MAGIC_COLUMNS
from .osv import fields
diff --git a/openerp/modules/module.py b/openerp/modules/module.py
index 18687e865c0..e50ba86fc39 100644
--- a/openerp/modules/module.py
+++ b/openerp/modules/module.py
@@ -46,7 +46,7 @@ class AddonsImportHook(object):
thus `import openerp.addons.module`.
"""
- def find_module(self, module_name, package_path):
+ def find_module(self, module_name, package_path=None):
module_parts = module_name.split('.')
if len(module_parts) == 3 and module_name.startswith('openerp.addons.'):
return self # We act as a loader too.
diff --git a/openerp/osv/fields.py b/openerp/osv/fields.py
index 294bf6a817b..9411c1e4df6 100644
--- a/openerp/osv/fields.py
+++ b/openerp/osv/fields.py
@@ -25,21 +25,18 @@ import pytz
import re
import xmlrpclib
from operator import itemgetter
-from contextlib import contextmanager
from psycopg2 import Binary
import openerp
import openerp.tools as tools
+from openerp.sql_db import LazyCursor
from openerp.tools.translate import _
from openerp.tools import float_repr, float_round, frozendict, html_sanitize
import json
-from openerp import SUPERUSER_ID, registry
+from openerp import SUPERUSER_ID
-@contextmanager
-def _get_cursor():
- # yield a valid cursor from any environment or create a new one if none found
- with registry().cursor() as cr:
- yield cr
+# deprecated; kept for backward compatibility only
+_get_cursor = LazyCursor
EMPTY_DICT = frozendict()
@@ -378,7 +375,7 @@ class float(_column):
@property
def digits(self):
if self._digits_compute:
- with _get_cursor() as cr:
+ with LazyCursor() as cr:
return self._digits_compute(cr)
else:
return self._digits
@@ -1354,7 +1351,7 @@ class function(_column):
@property
def digits(self):
if self._digits_compute:
- with _get_cursor() as cr:
+ with LazyCursor() as cr:
return self._digits_compute(cr)
else:
return self._digits
diff --git a/openerp/sql_db.py b/openerp/sql_db.py
index 749af2376c0..4aef9549ff4 100644
--- a/openerp/sql_db.py
+++ b/openerp/sql_db.py
@@ -456,6 +456,40 @@ class TestCursor(Cursor):
self.execute("ROLLBACK TO SAVEPOINT test_cursor")
self.execute("SAVEPOINT test_cursor")
+class LazyCursor(object):
+ """ A proxy object to a cursor. The cursor itself is allocated only if it is
+ needed. This class is useful for cached methods, that use the cursor
+ only in the case of a cache miss.
+ """
+ def __init__(self, dbname=None):
+ self._dbname = dbname
+ self._cursor = None
+ self._depth = 0
+
+ @property
+ def dbname(self):
+ return self._dbname or threading.currentThread().dbname
+
+ def __getattr__(self, name):
+ cr = self._cursor
+ if cr is None:
+ from openerp import registry
+ cr = self._cursor = registry(self.dbname).cursor()
+ for _ in xrange(self._depth):
+ cr.__enter__()
+ return getattr(cr, name)
+
+ def __enter__(self):
+ self._depth += 1
+ if self._cursor is not None:
+ self._cursor.__enter__()
+ return self
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ self._depth -= 1
+ if self._cursor is not None:
+ self._cursor.__exit__(exc_type, exc_value, traceback)
+
class PsycoConnection(psycopg2.extensions.connection):
pass