diff --git a/addons/portal_stock/security/portal_security.xml b/addons/portal_stock/security/portal_security.xml index 458de89b89b..2355a1331cf 100644 --- a/addons/portal_stock/security/portal_security.xml +++ b/addons/portal_stock/security/portal_security.xml @@ -6,7 +6,7 @@ Portal Follower Pickings - ['|', ('message_partner_ids','in',[user.partner_id.id]), ('partner_id.id','=',user.partner_id.id)] + ['|', '|', ('message_partner_ids', 'in', [user.partner_id.id]), ('partner_id.id', '=', user.partner_id.id), ('sale_id.partner_id.id', '=', user.partner_id.id)] diff --git a/addons/sale_stock/sale_stock.py b/addons/sale_stock/sale_stock.py index 5c5e789f63a..f046bea5678 100644 --- a/addons/sale_stock/sale_stock.py +++ b/addons/sale_stock/sale_stock.py @@ -303,7 +303,14 @@ class StockPicking(models.Model): break picking.sale_id = sale_order.id if sale_order else False - sale_id = fields.Many2one(comodel_name='sale.order', string="Sale Order", compute='_compute_sale_id') + def _search_sale_id(self, operator, value): + moves = self.env['stock.move'].search( + [('picking_id', '!=', False), ('procurement_id.sale_line_id.order_id', operator, value)] + ) + return [('id', 'in', moves.mapped('picking_id').ids)] + + sale_id = fields.Many2one(comodel_name='sale.order', string="Sale Order", + compute='_compute_sale_id', search='_search_sale_id') class AccountInvoiceLine(models.Model): diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 7dd472d29b7..02b1405e8ae 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -1440,10 +1440,11 @@ class stock_picking(models.Model): #Check moves with same product product_qty = ops.qty_done if done_qtys else ops.product_qty qty_to_assign = uom_obj._compute_qty_obj(cr, uid, ops.product_uom_id, product_qty, ops.product_id.uom_id, context=context) + precision_rounding = ops.product_id.uom_id.rounding for move_dict in prod2move_ids.get(ops.product_id.id, []): move = move_dict['move'] for quant in move.reserved_quant_ids: - if not qty_to_assign > 0: + if float_compare(qty_to_assign, 0, precision_rounding=precision_rounding) != 1: break if quant.id in quants_in_package_done: continue @@ -1466,7 +1467,7 @@ class stock_picking(models.Model): qty_to_assign -= qty_on_link lot_qty[quant.lot_id.id] -= qty_on_link - qty_assign_cmp = float_compare(qty_to_assign, 0, precision_rounding=ops.product_id.uom_id.rounding) + qty_assign_cmp = float_compare(qty_to_assign, 0, precision_rounding=precision_rounding) if qty_assign_cmp > 0: #qty reserved is less than qty put in operations. We need to create a link but it's deferred after we processed #all the quants (because they leave no choice on their related move and needs to be processed with higher priority) diff --git a/addons/stock/views/report_stockpicking_operations.xml b/addons/stock/views/report_stockpicking_operations.xml index 155524e5867..45e68fc7169 100644 --- a/addons/stock/views/report_stockpicking_operations.xml +++ b/addons/stock/views/report_stockpicking_operations.xml @@ -33,7 +33,7 @@
-

VAT:

+

VAT:

diff --git a/addons/web/static/src/js/views/form_relational_widgets.js b/addons/web/static/src/js/views/form_relational_widgets.js index a1444bcae85..5b0987f0fc0 100644 --- a/addons/web/static/src/js/views/form_relational_widgets.js +++ b/addons/web/static/src/js/views/form_relational_widgets.js @@ -886,6 +886,9 @@ var FieldX2Many = AbstractManyField.extend({ } return true; }, + is_false: function () { + return _(this.dataset.ids).isEmpty(); + }, }); var X2ManyDataSet = data.BufferedDataSet.extend({ diff --git a/doc/cla/corporate/acsone.md b/doc/cla/corporate/acsone.md index e1ab2495b1b..8e41f328f83 100644 --- a/doc/cla/corporate/acsone.md +++ b/doc/cla/corporate/acsone.md @@ -17,6 +17,7 @@ Anthony Muschang anthony.muschang@acsone.eu https://github.com/anthony-muschang Cédric Pigeon cedric.pigeon@acsone.eu https://github.com/Cedric-Pigeon Christelle De Coninck christelle.deconinck@acsone.eu https://github.com/chrisdec Jonathan Nemry jonathan.nemry@acsone.eu https://github.com/JonathanNEMRY +Denis Roussel denis.roussel@acsone.eu https://github.com/rousseldenis Laetia Gangloff laetitia.gangloff@acsone.eu https://github.com/laetitia-gangloff Laurent Mignon laurent.mignon@acsone.eu https://github.com/lmignon Olivier Laurent olivier.laurent@acsone.eu https://github.com/Olivier-LAURENT diff --git a/doc/cla/individual/engelant.md b/doc/cla/individual/engelant.md new file mode 100644 index 00000000000..05a5d7b77fa --- /dev/null +++ b/doc/cla/individual/engelant.md @@ -0,0 +1,9 @@ +Germany, 23 June 2016 + +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, + +Anton Engelhardt mumie_die@yahoo.de https://github.com/engelant diff --git a/openerp/addons/test_new_api/ir.model.access.csv b/openerp/addons/test_new_api/ir.model.access.csv index 60cf53bcfa4..a8d921c30ae 100644 --- a/openerp/addons/test_new_api/ir.model.access.csv +++ b/openerp/addons/test_new_api/ir.model.access.csv @@ -11,3 +11,5 @@ access_mixed,test_new_api_mixed,test_new_api.model_test_new_api_mixed,,1,1,1,1 access_test_function_noinfiniterecursion,access_test_function_noinfiniterecursion,model_test_old_api_function_noinfiniterecursion,,1,1,1,1 access_test_function_counter,access_test_function_counter,model_test_old_api_function_counter,,1,1,1,1 access_domain_bool,access_domain_bool,model_domain_bool,,1,1,1,1 +access_test_new_api_foo,access_test_new_api_foo,model_test_new_api_foo,,1,1,1,1 +access_test_new_api_bar,access_test_new_api_bar,model_test_new_api_bar,,1,1,1,1 diff --git a/openerp/addons/test_new_api/models.py b/openerp/addons/test_new_api/models.py index 20b3a9f4bd3..adcf3baac64 100644 --- a/openerp/addons/test_new_api/models.py +++ b/openerp/addons/test_new_api/models.py @@ -284,3 +284,25 @@ class BoolModel(models.Model): bool_true = fields.Boolean('b1', default=True) bool_false = fields.Boolean('b2', default=False) bool_undefined = fields.Boolean('b3') + + +class Foo(models.Model): + _name = 'test_new_api.foo' + + name = fields.Char() + value1 = fields.Integer() + value2 = fields.Integer() + + +class Bar(models.Model): + _name = 'test_new_api.bar' + + name = fields.Char() + foo = fields.Many2one('test_new_api.foo', compute='_compute_foo') + value1 = fields.Integer(related='foo.value1') + value2 = fields.Integer(related='foo.value2') + + @api.depends('name') + def _compute_foo(self): + for bar in self: + bar.foo = self.env['test_new_api.foo'].search([('name', '=', bar.name)], limit=1) 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 24706c658fb..63a164ab17f 100644 --- a/openerp/addons/test_new_api/tests/test_new_fields.py +++ b/openerp/addons/test_new_api/tests/test_new_fields.py @@ -361,6 +361,19 @@ class TestNewFields(common.TransactionCase): discussion_field = discussion.fields_get(['name'])['name'] self.assertEqual(message_field['help'], discussion_field['help']) + def test_25_related_multi(self): + """ test write() on several related fields based on a common computed field. """ + foo = self.env['test_new_api.foo'].create({'name': 'A', 'value1': 1, 'value2': 2}) + bar = self.env['test_new_api.bar'].create({'name': 'A'}) + self.assertEqual(bar.foo, foo) + self.assertEqual(bar.value1, 1) + self.assertEqual(bar.value2, 2) + + foo.invalidate_cache() + bar.write({'value1': 3, 'value2': 4}) + self.assertEqual(foo.value1, 3) + self.assertEqual(foo.value2, 4) + def test_26_inherited(self): """ test inherited fields. """ # a bunch of fields are inherited from res_partner diff --git a/openerp/models.py b/openerp/models.py index de9158b5da8..648ff81dacd 100644 --- a/openerp/models.py +++ b/openerp/models.py @@ -3836,12 +3836,18 @@ class BaseModel(object): if old_vals: self._write(old_vals) - # put the values of pure new-style fields into cache, and inverse them if new_vals: + # put the values of pure new-style fields into cache for record in self: record._cache.update(record._convert_to_cache(new_vals, update=True)) + # mark the fields as being computed, to avoid their invalidation + for key in new_vals: + self.env.computed[self._fields[key]].update(self._ids) + # inverse the fields for key in new_vals: self._fields[key].determine_inverse(self) + for key in new_vals: + self.env.computed[self._fields[key]].difference_update(self._ids) return True @@ -4150,10 +4156,16 @@ class BaseModel(object): # create record with old-style fields record = self.browse(self._create(old_vals)) - # put the values of pure new-style fields into cache, and inverse them + # put the values of pure new-style fields into cache record._cache.update(record._convert_to_cache(new_vals)) + # mark the fields as being computed, to avoid their invalidation + for key in new_vals: + self.env.computed[self._fields[key]].add(record.id) + # inverse the fields for key in new_vals: self._fields[key].determine_inverse(record) + for key in new_vals: + self.env.computed[self._fields[key]].discard(record.id) return record