I agree that by signing this proposal, I - accept it on the behalf of , for an amount of + accept it on the behalf of , for an amount of with payment terms: . @@ -325,7 +325,7 @@
I agree that by paying this proposal, I - accept it on the behalf of , for an amount of + accept it on the behalf of , for an amount of with payment terms: . diff --git a/addons/website_sale/models/product.py b/addons/website_sale/models/product.py index 9e53f039407..d5a8f77a72d 100644 --- a/addons/website_sale/models/product.py +++ b/addons/website_sale/models/product.py @@ -207,7 +207,6 @@ class Product(models.Model): self.ensure_one() return self.product_tmpl_id.website_publish_button() - class ProductAttribute(models.Model): _inherit = "product.attribute" diff --git a/addons/website_slides/models/slides.py b/addons/website_slides/models/slides.py index b6dc690cc99..d02da6b19cb 100644 --- a/addons/website_slides/models/slides.py +++ b/addons/website_slides/models/slides.py @@ -164,6 +164,20 @@ class Channel(models.Model): self.with_context(active_test=False).mapped('slide_ids').write({'active': vals['active']}) return res + @api.multi + def message_post(self, parent_id=False, subtype=None, **kwargs): + """ Temporary workaround to avoid spam. If someone replies on a channel + through the 'Presentation Published' email, it should be considered as a + note as we don't want all channel followers to be notified of this answer. """ + self.ensure_one() + if parent_id: + parent_message = self.env['mail.message'].sudo().browse(parent_id) + if parent_message.subtype_id and parent_message.subtype_id == self.env.ref('website_slides.mt_channel_slide_published'): + if kwargs.get('subtype_id'): + kwargs['subtype_id'] = False + subtype = 'mail.mt_note' + return super(Channel, self).message_post(parent_id=parent_id, subtype=subtype, **kwargs) + class Category(models.Model): """ Channel contain various categories to manage its slides """ diff --git a/odoo/addons/test_new_api/tests/__init__.py b/odoo/addons/test_new_api/tests/__init__.py index 2bbcfe40990..b90ccc96825 100644 --- a/odoo/addons/test_new_api/tests/__init__.py +++ b/odoo/addons/test_new_api/tests/__init__.py @@ -3,5 +3,6 @@ from . import test_new_fields from . import test_onchange from . import test_attributes +from . import test_one2many from . import test_ui from . import test_domain diff --git a/odoo/addons/test_new_api/tests/test_onchange.py b/odoo/addons/test_new_api/tests/test_onchange.py index 87fc8229b7c..a6e2bff1b35 100644 --- a/odoo/addons/test_new_api/tests/test_onchange.py +++ b/odoo/addons/test_new_api/tests/test_onchange.py @@ -176,7 +176,7 @@ class TestOnChange(common.TransactionCase): self.assertEqual(values, { 'name': partner.name, 'partner': partner.id, - 'lines': [(5,), (4, line.id)], + 'lines': [(6, 0, [line.id])], }) # modify 'partner' diff --git a/odoo/fields.py b/odoo/fields.py index ee732b5b62f..c542945a781 100644 --- a/odoo/fields.py +++ b/odoo/fields.py @@ -894,10 +894,12 @@ class Field(object): env.invalidate(spec) else: - # simply write to the database, and update cache + # Write to database write_value = self.convert_to_write(self.convert_to_record(value, record), record) record.write({self.name: write_value}) - record._cache[self] = value + # Update the cache unless value contains a new record + if not (self.relational and not all(value)): + record._cache[self] = value ############################################################################ # @@ -1987,7 +1989,7 @@ class _RelationalMulti(_Relational): def convert_to_write(self, value, record): # make result with new and existing records - result = [(5,)] + result = [(6, 0, [])] for record in value: if not record.id: values = {name: record[name] for name in record._cache} @@ -1998,7 +2000,7 @@ class _RelationalMulti(_Relational): values = record._convert_to_write(values) result.append((1, record.id, values)) else: - result.append((4, record.id)) + result[0][2].append(record.id) return result def convert_to_onchange(self, value, record, fnames=()): @@ -2171,7 +2173,11 @@ class One2many(_RelationalMulti): query = "SELECT id FROM %s WHERE %s=%%s AND id <> ALL(%%s)" % (comodel._table, inverse) comodel._cr.execute(query, (record.id, act[2] or [0])) lines = comodel.browse([row[0] for row in comodel._cr.fetchall()]) - lines.write({inverse: False}) + inverse_field = comodel._fields[inverse] + if inverse_field.ondelete == 'cascade': + lines.unlink() + else: + lines.write({inverse: False}) class Many2many(_RelationalMulti): diff --git a/odoo/models.py b/odoo/models.py index 714cde7ff0b..8f15a59d7cd 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -3508,8 +3508,7 @@ class BaseModel(object): ``(6, _, ids)`` replaces all existing records in the set by the ``ids`` list, equivalent to using the command ``5`` followed by a command - ``4`` for each ``id`` in ``ids``. Can not be used on - :class:`~odoo.fields.One2many`. + ``4`` for each ``id`` in ``ids``. .. note:: Values marked as ``_`` in the list above are ignored and can be anything, generally ``0`` or ``False``. diff --git a/openerp/addons/test_new_api/tests/test_one2many.py b/openerp/addons/test_new_api/tests/test_one2many.py new file mode 100644 index 00000000000..38dee13ab54 --- /dev/null +++ b/openerp/addons/test_new_api/tests/test_one2many.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +from openerp.tests.common import TransactionCase + + +class One2manyCase(TransactionCase): + def setUp(self): + super(One2manyCase, self).setUp() + self.Line = self.env["test_new_api.multi.line"] + self.multi = self.env["test_new_api.multi"].create({ + "name": "What is up?" + }) + + def operations(self): + """Run operations on o2m fields to check all works fine.""" + # Check the lines first + self.assertItemsEqual( + self.multi.lines.mapped('name'), + map(str, range(10))) + # Modify the first line and drop the last one + self.multi.lines[0].name = "hello" + self.multi.lines = self.multi.lines[:-1] + self.assertEqual(len(self.multi.lines), 9) + self.assertIn("hello", self.multi.lines.mapped('name')) + # Invalidate the cache and check again; this crashes if the value + # of self.multi.lines in cache contains new records + self.multi.invalidate_cache() + self.assertEqual(len(self.multi.lines), 9) + self.assertIn("hello", self.multi.lines.mapped('name')) + + def test_new_one_by_one(self): + """Check lines created with ``new()`` and appended one by one.""" + for name in range(10): + self.multi.lines |= self.Line.new({"name": str(name)}) + self.operations() + + def test_new_single(self): + """Check lines created with ``new()`` and added in one step.""" + self.multi.lines = self.Line.browse( + [self.Line.new({"name": str(name)}).id for name in range(10)] + ) + self.operations() + + def test_create_one_by_one(self): + """Check lines created with ``create()`` and appended one by one.""" + for name in range(10): + self.multi.lines |= self.Line.create({"name": str(name)}) + self.operations() + + def test_create_single(self): + """Check lines created with ``create()`` and added in one step.""" + self.multi.lines = self.Line.browse( + [self.Line.create({"name": str(name)}).id for name in range(10)] + ) + self.operations() + + def test_rpcstyle_one_by_one(self): + """Check lines created with RPC style and appended one by one.""" + for name in range(10): + self.multi.lines = [(0, 0, {"name": str(name)})] + self.operations() + + def test_rpcstyle_single(self): + """Check lines created with RPC style and added in one step""" + self.multi.lines = [(0, 0, {'name': str(name)}) for name in range(10)] + self.operations()