[MERGE] forward port of branch saas-6 up to 2616def

This commit is contained in:
Denis Ledoux
2016-01-29 12:29:17 +01:00
10 changed files with 97 additions and 32 deletions
+1 -1
View File
@@ -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 """
+2
View File
@@ -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')
@@ -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>
@@ -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));
+1 -1
View File
@@ -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
@@ -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']
+16 -5
View File
@@ -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
+1 -1
View File
@@ -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.
+6 -9
View File
@@ -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
+34
View File
@@ -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