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/hr_timesheet_sheet/hr_timesheet_sheet_view.xml b/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml
index f924d3ec76d..e52725a7270 100644
--- a/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml
+++ b/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml
@@ -262,16 +262,8 @@
Invoice and shipping address:
-Invoice address:
- -VAT:
+ Invoice and shipping address: +VAT:
+Shipping address :
+ Shipping address :VAT:
diff --git a/addons/portal_sale/portal_sale.py b/addons/portal_sale/portal_sale.py index b4867c539ad..4442c9329a9 100644 --- a/addons/portal_sale/portal_sale.py +++ b/addons/portal_sale/portal_sale.py @@ -28,7 +28,8 @@ class sale_order(osv.Model): )[document.partner_id.id] def get_formview_action(self, cr, uid, id, context=None): - user = self.pool['res.users'].browse(cr, SUPERUSER_ID, uid, context=context) + context = context or {} + user = self.pool['res.users'].browse(cr, SUPERUSER_ID, context.get('uid', uid), context=context) if user.share: document = self.browse(cr, uid, id, context=context) action_xmlid = 'action_quotations_portal' if document.state in ('draft', 'sent') else 'action_orders_portal' @@ -57,7 +58,8 @@ class account_invoice(osv.Model): )[document.partner_id.id] def get_formview_action(self, cr, uid, id, context=None): - user = self.pool['res.users'].browse(cr, SUPERUSER_ID, uid, context=context) + context = context or {} + user = self.pool['res.users'].browse(cr, SUPERUSER_ID, context.get('uid', uid), context=context) if user.share: return self.pool['ir.actions.act_window'].for_xml_id(cr, uid, 'portal_sale', 'portal_action_invoices', context=context) return super(account_invoice, self).get_formview_action(cr, uid, id, context=context) diff --git a/addons/purchase/purchase_view.xml b/addons/purchase/purchase_view.xml index cd171a98785..55dc320ac8e 100644 --- a/addons/purchase/purchase_view.xml +++ b/addons/purchase/purchase_view.xml @@ -145,7 +145,7 @@ - + diff --git a/addons/stock/i18n/stock.pot b/addons/stock/i18n/stock.pot index 5fe7bccf6d6..f00859e3af2 100644 --- a/addons/stock/i18n/stock.pot +++ b/addons/stock/i18n/stock.pot @@ -51,6 +51,12 @@ msgstr "" msgid " (reserved)" msgstr "" +#. module: stock +#: code:addons/stock/stock.py:3182 +#, python-format +msgid "You cannot have two inventory adjustements in state 'in Progess' with the same product(%s), same location(%s), same package, same owner and same lot. Please first validate the first inventory adjustement with this product before creating another one." +msgstr "" + #. module: stock #: code:addons/stock/stock.py:3482 #, python-format diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 483d2fe30ed..a70245dd88c 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -3172,6 +3172,14 @@ class stock_inventory_line(osv.osv): if context is None: context = {} product_obj = self.pool.get('product.product') + dom = [('product_id', '=', values.get('product_id')), ('inventory_id.state', '=', 'confirm'), + ('location_id', '=', values.get('location_id')), ('partner_id', '=', values.get('partner_id')), + ('package_id', '=', values.get('package_id')), ('prod_lot_id', '=', values.get('prod_lot_id'))] + res = self.search(cr, uid, dom, context=context) + if res: + location = self.pool['stock.location'].browse(cr, uid, values.get('location_id'), context=context) + product = product_obj.browse(cr, uid, values.get('product_id'), context=context) + raise UserError(_("You cannot have two inventory adjustements in state 'in Progess' with the same product(%s), same location(%s), same package, same owner and same lot. Please first validate the first inventory adjustement with this product before creating another one.") % (product.name, location.name)) if 'product_id' in values and not 'product_uom_id' in values: values['product_uom_id'] = product_obj.browse(cr, uid, values.get('product_id'), context=context).uom_id.id return super(stock_inventory_line, self).create(cr, uid, values, context=context) 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 1e598b9a937..112064e743f 100644 --- a/addons/website/models/website.py +++ b/addons/website/models/website.py @@ -478,7 +478,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 4c34f4a0b96..a68b1ee1ad4 100644 --- a/openerp/modules/module.py +++ b/openerp/modules/module.py @@ -91,7 +91,7 @@ class OdooHook(object): """ Makes odoo package also available as openerp """ - def find_module(self, name, path): + def find_module(self, name, path=None): # openerp.addons.