summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2017-01-27 18:20:28 +0100
committerChristophe Simonis <chs@odoo.com>2017-01-27 18:20:28 +0100
commitba8a1c7ec24cba065934e8eb58f97d15bb60b55f (patch)
tree40a11ec3029046caa11a9a16dd650d10c684ce5c
parentcd605442aef46d9753ca46e80d76363730d2d966 (diff)
parente86983112eb1195e129f0c13e0a426011ebb4bf2 (diff)
[MERGE] forward port branch saas-11 up to e86983112eb
-rw-r--r--addons/account/data/account_data.xml1
-rw-r--r--addons/account/models/account_invoice.py3
-rw-r--r--addons/account/views/account_invoice_view.xml2
-rw-r--r--addons/hr/models/hr.py4
-rw-r--r--addons/hr/models/res_partner.py5
-rw-r--r--addons/hr/views/hr_views.xml2
-rw-r--r--addons/hw_escpos/escpos/printer.py2
-rw-r--r--addons/l10n_be_invoice_bba/models/account_invoice.py2
-rw-r--r--addons/mass_mailing/wizard/test_mailing.py2
-rw-r--r--addons/stock/models/stock_picking.py22
-rw-r--r--addons/stock/tests/test_stock_flow.py193
-rw-r--r--addons/web/static/src/xml/base.xml7
-rw-r--r--addons/web/views/webclient_templates.xml2
-rw-r--r--addons/website_sale_options/static/src/js/website_sale.js9
-rw-r--r--doc/cla/corporate/camptocamp.md1
-rw-r--r--doc/cla/corporate/teclib.md1
-rw-r--r--doc/cla/individual/halgandd.md11
-rw-r--r--odoo/addons/base/ir/ir_actions.py1
-rw-r--r--odoo/addons/test_inherit/tests/test_inherit.py6
-rw-r--r--odoo/osv/expression.py9
20 files changed, 253 insertions, 32 deletions
diff --git a/addons/account/data/account_data.xml b/addons/account/data/account_data.xml
index fc1d1bae318..3beea73a028 100644
--- a/addons/account/data/account_data.xml
+++ b/addons/account/data/account_data.xml
@@ -51,6 +51,7 @@
<field name="prefix">A</field>
<field eval="1" name="number_next"/>
<field eval="1" name="number_increment"/>
+ <field eval="False" name="company_id"/>
</record>
<!--
Invoice requests (deprecated)
diff --git a/addons/account/models/account_invoice.py b/addons/account/models/account_invoice.py
index a3bd8de17ca..499748f3692 100644
--- a/addons/account/models/account_invoice.py
+++ b/addons/account/models/account_invoice.py
@@ -769,8 +769,9 @@ class AccountInvoice(models.Model):
will be grouped together if the journal has the 'group line' option. Of course a module
can add fields to invoice lines that would need to be tested too before merging lines
or not."""
- return "%s-%s-%s-%s-%s-%s" % (
+ return "%s-%s-%s-%s-%s-%s-%s" % (
invoice_line['account_id'],
+ invoice_line.get('tax_ids', 'False'),
invoice_line.get('tax_line_id', 'False'),
invoice_line.get('product_id', 'False'),
invoice_line.get('analytic_account_id', 'False'),
diff --git a/addons/account/views/account_invoice_view.xml b/addons/account/views/account_invoice_view.xml
index 500044ecc96..bcd4113bec9 100644
--- a/addons/account/views/account_invoice_view.xml
+++ b/addons/account/views/account_invoice_view.xml
@@ -61,7 +61,7 @@
<group>
<group>
<field name="product_id"
- context="{'partner_id': parent.partner_id}"/>
+ context="parent and {'partner_id': parent.partner_id}"/>
<label for="quantity"/>
<div>
<field name="quantity" class="oe_inline"/>
diff --git a/addons/hr/models/hr.py b/addons/hr/models/hr.py
index 97c72379db7..1c44ee48064 100644
--- a/addons/hr/models/hr.py
+++ b/addons/hr/models/hr.py
@@ -193,6 +193,10 @@ class Employee(models.Model):
@api.multi
def write(self, vals):
+ if 'address_home_id' in vals:
+ account_id = vals.get('bank_account_id') or self.bank_account_id.id
+ if account_id:
+ self.env['res.partner.bank'].browse(account_id).partner_id = vals['address_home_id']
tools.image_resize_images(vals)
return super(Employee, self).write(vals)
diff --git a/addons/hr/models/res_partner.py b/addons/hr/models/res_partner.py
index 8772bc46040..f0b24ba42b5 100644
--- a/addons/hr/models/res_partner.py
+++ b/addons/hr/models/res_partner.py
@@ -16,8 +16,9 @@ class Partner(models.Model):
try:
employee_group = self.env.ref('base.group_user')
- for user in employee_group.users:
- suggestions.append((user.partner_id.id, user.name, user.email))
+ hr_suggestions = [{'id': user.partner_id.id, 'name': user.name, 'email': user.email}
+ for user in employee_group.users]
+ suggestions.append(hr_suggestions)
return suggestions
except AccessError:
return suggestions
diff --git a/addons/hr/views/hr_views.xml b/addons/hr/views/hr_views.xml
index fe856d2961e..d48bd650436 100644
--- a/addons/hr/views/hr_views.xml
+++ b/addons/hr/views/hr_views.xml
@@ -80,7 +80,7 @@
<field name="country_id" options='{"no_open": True, "no_create": True}'/>
<field name="identification_id" groups="hr.group_hr_user"/>
<field name="passport_id" groups="hr.group_hr_user"/>
- <field name="bank_account_id"/>
+ <field name="bank_account_id" context="{'default_partner_id': address_home_id}"/>
</group>
<group string="Contact Information">
<field name="address_home_id"
diff --git a/addons/hw_escpos/escpos/printer.py b/addons/hw_escpos/escpos/printer.py
index f994d946a02..e857fa2e365 100644
--- a/addons/hw_escpos/escpos/printer.py
+++ b/addons/hw_escpos/escpos/printer.py
@@ -82,7 +82,7 @@ class Usb(Escpos):
def _raw(self, msg):
""" Print any command sent in raw format """
- if len(msg) != self.device.write(self.out_ep, msg, self.interface):
+ if len(msg) != self.device.write(self.out_ep, msg, self.interface, timeout=5000):
self.device.write(self.out_ep, self.errorText, self.interface)
raise TicketNotPrinted()
diff --git a/addons/l10n_be_invoice_bba/models/account_invoice.py b/addons/l10n_be_invoice_bba/models/account_invoice.py
index e82f2e9a20b..9bf72ae7b0f 100644
--- a/addons/l10n_be_invoice_bba/models/account_invoice.py
+++ b/addons/l10n_be_invoice_bba/models/account_invoice.py
@@ -23,7 +23,7 @@ class AccountInvoice(models.Model):
def _get_reference_type(self):
"""Add BBA Structured Communication Type and change labels from 'reference' into 'communication' """
res = super(AccountInvoice, self)._get_reference_type()
- res[[i for i, x in enumerate(res) if x[0] == 'none'][0]] = ('none', 'Free Communication')
+ res[[i for i, x in enumerate(res) if x[0] == 'none'][0]] = ('none', _('Free Communication'))
res.append(('bba', 'BBA Structured Communication'))
return res
diff --git a/addons/mass_mailing/wizard/test_mailing.py b/addons/mass_mailing/wizard/test_mailing.py
index 17473464126..130827e1fe4 100644
--- a/addons/mass_mailing/wizard/test_mailing.py
+++ b/addons/mass_mailing/wizard/test_mailing.py
@@ -10,7 +10,7 @@ class TestMassMailing(models.TransientModel):
email_to = fields.Char(string='Recipients', required=True,
help='Comma-separated list of email addresses.', default=lambda self: self.env['mail.message']._get_default_from())
- mass_mailing_id = fields.Many2one('mail.mass_mailing', string='Mailing', required=True)
+ mass_mailing_id = fields.Many2one('mail.mass_mailing', string='Mailing', required=True, ondelete='cascade')
@api.multi
def send_mail_test(self):
diff --git a/addons/stock/models/stock_picking.py b/addons/stock/models/stock_picking.py
index 7e1d54b9aa5..55ac54287a5 100644
--- a/addons/stock/models/stock_picking.py
+++ b/addons/stock/models/stock_picking.py
@@ -316,22 +316,18 @@ class Picking(models.Model):
self.state = 'cancel'
elif all(move.state in ['cancel', 'done'] for move in self.move_lines):
self.state = 'done'
- elif self.move_type == 'one':
- ordered_moves = self.move_lines.filtered(
- lambda move: move.state not in ['cancel', 'done']
- ).sorted(
- key=lambda move: (move.state == 'assigned' and 2) or (move.state == 'waiting' and 1) or 0, reverse=False
- )
- self.state = ordered_moves[0].state
else:
- filtered_moves = self.move_lines.filtered(lambda move: move.state not in ['cancel', 'done'])
- if not all(move.state == 'assigned' for move in filtered_moves) and any(move.state == 'assigned' for move in filtered_moves):
- self.state = 'partially_available'
- elif any(move.partially_available for move in filtered_moves):
+ # We sort our moves by importance of state: "confirmed" should be first, then we'll have
+ # "waiting" and finally "assigned" at the end.
+ moves_todo = self.move_lines\
+ .filtered(lambda move: move.state not in ['cancel', 'done'])\
+ .sorted(key=lambda move: (move.state == 'assigned' and 2) or (move.state == 'waiting' and 1) or 0)
+ if self.move_type == 'one':
+ self.state = moves_todo[0].state
+ elif moves_todo[0].state != 'assigned' and any(x.partially_available or x.state == 'assigned' for x in moves_todo):
self.state = 'partially_available'
else:
- ordered_moves = filtered_moves.sorted(key=lambda move: (move.state == 'assigned' and 2) or (move.state == 'waiting' and 1) or 0, reverse=True)
- self.state = ordered_moves[0].state
+ self.state = moves_todo[-1].state
@api.one
@api.depends('move_lines.priority')
diff --git a/addons/stock/tests/test_stock_flow.py b/addons/stock/tests/test_stock_flow.py
index 65d2a862866..e0ddb97a84e 100644
--- a/addons/stock/tests/test_stock_flow.py
+++ b/addons/stock/tests/test_stock_flow.py
@@ -1210,7 +1210,6 @@ class TestStockFlow(TestStockCommon):
inventory5.action_done()
self.assertEqual(inventory5_lines_count, inventory4_lines_count + 1, "The new product is not taken into account in the inventory valuation.")
self.assertTrue(product.id in inventory5.line_ids.mapped('product_id').ids, "The new product is not take into account in the inventory valuation.")
-
def test_30_check_with_no_incoming_lot(self):
""" Picking in without lots and picking out with"""
@@ -1278,7 +1277,6 @@ class TestStockFlow(TestStockCommon):
self.assertEqual(sum([x.qty for x in quants if x.lot_id.id == lot2.id]), 1.0, 'Wrong sum of quants with lot 2')
self.assertEqual(sum([x.qty for x in quants if x.lot_id.id == lot3.id]), 2.0, 'Wrong sum of quants with lot 3')
-
def test_40_pack_in_pack(self):
""" Put a pack in pack"""
picking_out = self.PickingObj.create({
@@ -1536,3 +1534,194 @@ class TestStockFlow(TestStockCommon):
# We should also make sure that when matching stock moves with pack operations, it takes the correct
self.assertEqual(len(picking_out.move_lines[0].linked_move_operation_ids), 2, 'We should only have 2 links beween the move and the 2 operations')
self.assertEqual(len(picking_out.move_lines[0].quant_ids), 2, 'We should have exactly 2 quants in the end')
+
+ def test_70_picking_state_all_at_once_reserve(self):
+ """ This test will check that the state of the picking is correctly computed according
+ to the state of its move lines and its move type.
+ """
+ # move_type: direct == partial, one == all at once
+ # picking: confirmed == waiting availability, partially_available = partially available
+
+ # -----------------------------------------------------------
+ # "all at once" and "reserve" scenario
+ # -----------------------------------------------------------
+ # get one product in stock
+ inventory = self.env['stock.inventory'].create({
+ 'name': 'Inventory Product Table',
+ 'filter': 'partial',
+ 'line_ids': [(0, 0, {
+ 'product_id': self.productA.id,
+ 'product_uom_id': self.productA.uom_id.id,
+ 'product_qty': 1,
+ 'location_id': self.stock_location
+ })]
+ })
+ inventory.action_done()
+
+ # create a "all at once" delivery order for two products
+ picking_out = self.PickingObj.create({
+ 'partner_id': self.partner_agrolite_id,
+ 'picking_type_id': self.picking_type_out,
+ 'location_id': self.stock_location,
+ 'location_dest_id': self.customer_location})
+ picking_out.move_type = 'one'
+
+ self.MoveObj.create({
+ 'name': self.productA.name,
+ 'product_id': self.productA.id,
+ 'product_uom_qty': 2,
+ 'product_uom': self.productA.uom_id.id,
+ 'picking_id': picking_out.id,
+ 'location_id': self.stock_location,
+ 'location_dest_id': self.customer_location})
+
+ # validate this delivery order, it should be in the waiting state
+ picking_out.action_assign()
+ self.assertEquals(picking_out.state, "confirmed")
+
+ # receive one product in stock
+ inventory = self.env['stock.inventory'].create({
+ 'name': 'Inventory Product Table',
+ 'filter': 'partial',
+ 'line_ids': [(0, 0, {
+ 'product_id': self.productA.id,
+ 'product_uom_id': self.productA.uom_id.id,
+ 'product_qty': 2,
+ 'location_id': self.stock_location
+ })]
+ })
+ inventory.action_done()
+
+ # recheck availability of the delivery order, it should be assigned
+ picking_out.action_assign()
+ self.assertEquals(picking_out.state, "assigned")
+
+ def test_71_picking_state_all_at_once_force_assign(self):
+ """ This test will check that the state of the picking is correctly computed according
+ to the state of its move lines and its move type.
+ """
+ # move_type: direct == partial, one == all at once
+ # picking: confirmed == waiting availability, partially_available = partially available
+
+ # -----------------------------------------------------------
+ # "all at once" and "force assign" scenario
+ # -----------------------------------------------------------
+ # create a "all at once" delivery order for two products
+ picking_out = self.PickingObj.create({
+ 'partner_id': self.partner_agrolite_id,
+ 'picking_type_id': self.picking_type_out,
+ 'location_id': self.stock_location,
+ 'location_dest_id': self.customer_location})
+ picking_out.move_type = 'direct'
+
+ self.MoveObj.create({
+ 'name': self.productA.name,
+ 'product_id': self.productA.id,
+ 'product_uom_qty': 2,
+ 'product_uom': self.productA.uom_id.id,
+ 'picking_id': picking_out.id,
+ 'location_id': self.stock_location,
+ 'location_dest_id': self.customer_location})
+
+ # validate this delivery order, it should be in the waiting state
+ picking_out.action_assign()
+ self.assertEquals(picking_out.state, "confirmed")
+
+ # force assign on the delivery order, it should be assigned
+ picking_out.force_assign()
+ self.assertEquals(picking_out.state, "assigned")
+
+ def test_72_picking_state_partial_reserve(self):
+ """ This test will check that the state of the picking is correctly computed according
+ to the state of its move lines and its move type.
+ """
+ # move_type: direct == partial, one == all at once
+ # picking: confirmed == waiting availability, partially_available = partially available
+
+ # -----------------------------------------------------------
+ # "partial" and "reserve" scenario
+ # -----------------------------------------------------------
+ # get one product in stock
+ inventory = self.env['stock.inventory'].create({
+ 'name': 'Inventory Product Table',
+ 'filter': 'partial',
+ 'line_ids': [(0, 0, {
+ 'product_id': self.productA.id,
+ 'product_uom_id': self.productA.uom_id.id,
+ 'product_qty': 1,
+ 'location_id': self.stock_location
+ })]
+ })
+ inventory.action_done()
+
+ # create a "partial" delivery order for two products
+ picking_out = self.PickingObj.create({
+ 'partner_id': self.partner_agrolite_id,
+ 'picking_type_id': self.picking_type_out,
+ 'location_id': self.stock_location,
+ 'location_dest_id': self.customer_location})
+ picking_out.move_type = 'direct'
+
+ self.MoveObj.create({
+ 'name': self.productA.name,
+ 'product_id': self.productA.id,
+ 'product_uom_qty': 2,
+ 'product_uom': self.productA.uom_id.id,
+ 'picking_id': picking_out.id,
+ 'location_id': self.stock_location,
+ 'location_dest_id': self.customer_location})
+
+ # validate this delivery order, it should be in partially available
+ picking_out.action_assign()
+ self.assertEquals(picking_out.state, "partially_available")
+
+ # receive one product in stock
+ inventory = self.env['stock.inventory'].create({
+ 'name': 'Inventory Product Table',
+ 'filter': 'partial',
+ 'line_ids': [(0, 0, {
+ 'product_id': self.productA.id,
+ 'product_uom_id': self.productA.uom_id.id,
+ 'product_qty': 2,
+ 'location_id': self.stock_location
+ })]
+ })
+ inventory.action_done()
+
+ # recheck availability of the delivery order, it should be assigned
+ picking_out.action_assign()
+ self.assertEquals(picking_out.state, "assigned")
+
+ def test_73_picking_state_partial_force_assign(self):
+ """ This test will check that the state of the picking is correctly computed according
+ to the state of its move lines and its move type.
+ """
+ # move_type: direct == partial, one == all at once
+ # picking: confirmed == waiting availability, partially_available = partially available
+
+ # -----------------------------------------------------------
+ # "partial" and "force assign" scenario
+ # -----------------------------------------------------------
+ picking_out = self.PickingObj.create({
+ 'partner_id': self.partner_agrolite_id,
+ 'picking_type_id': self.picking_type_out,
+ 'location_id': self.stock_location,
+ 'location_dest_id': self.customer_location})
+ picking_out.move_type = 'direct'
+
+ self.MoveObj.create({
+ 'name': self.productA.name,
+ 'product_id': self.productA.id,
+ 'product_uom_qty': 2,
+ 'product_uom': self.productA.uom_id.id,
+ 'picking_id': picking_out.id,
+ 'location_id': self.stock_location,
+ 'location_dest_id': self.customer_location})
+
+ # validate this delivery order, it should be in the waiting state
+ picking_out.action_assign()
+ self.assertEquals(picking_out.state, "confirmed")
+
+ # force assign on the delivery order, it should be assigned
+ picking_out.force_assign()
+ self.assertEquals(picking_out.state, "assigned") \ No newline at end of file
diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml
index 11af5272077..e98251c5410 100644
--- a/addons/web/static/src/xml/base.xml
+++ b/addons/web/static/src/xml/base.xml
@@ -40,15 +40,15 @@
<table class="o_group o_inner_group o_label_nowrap">
<tr>
<td class="o_td_label"><label for="old_pwd" class="o_form_label">Old Password</label></td>
- <td width="100%"><input type="password" class="o_form_field o_form_input" name="old_pwd" minlength="1" autofocus="autofocus"/></td>
+ <td width="100%"><input type="password" class="o_form_field o_form_input" name="old_pwd" minlength="1" autofocus="autofocus" autocomplete="current-password"/></td>
</tr>
<tr>
<td class="o_td_label"><label for="new_password" class="o_form_label">New Password</label></td>
- <td width="100%"><input type="password" class="o_form_field o_form_input" name="new_password" minlength="1"/></td>
+ <td width="100%"><input type="password" class="o_form_field o_form_input" name="new_password" minlength="1" autocomplete="new-password"/></td>
</tr>
<tr>
<td class="o_td_label"><label for="confirm_pwd" class="o_form_label">Confirm New Password</label></td>
- <td width="100%"><input type="password" class="o_form_field o_form_input" name="confirm_pwd" minlength="1"/></td>
+ <td width="100%"><input type="password" class="o_form_field o_form_input" name="confirm_pwd" minlength="1" autocomplete="new-password"/></td>
</tr>
</table>
@@ -625,6 +625,7 @@
t-att-tabindex="widget.node.attrs.tabindex"
t-att-autofocus="widget.node.attrs.autofocus"
t-att-placeholder="widget.node.attrs.placeholder"
+ t-att-autocomplete="widget.password ? 'new-password' : widget.node.attrs.autocomplete"
t-att-maxlength="widget.field.size"/>
</t>
<t t-name="FormSelection">
diff --git a/addons/web/views/webclient_templates.xml b/addons/web/views/webclient_templates.xml
index 566b882b5ac..cffc4321808 100644
--- a/addons/web/views/webclient_templates.xml
+++ b/addons/web/views/webclient_templates.xml
@@ -262,7 +262,7 @@
<div class="form-group field-password">
<label for="password" class="control-label">Password</label>
- <input type="password" name="password" id="password" class="form-control" required="required" t-att-autofocus="'autofocus' if login else None" maxlength="4096"/>
+ <input type="password" name="password" id="password" class="form-control" required="required" autocomplete="current-password" t-att-autofocus="'autofocus' if login else None" maxlength="4096"/>
</div>
<p class="alert alert-danger" t-if="error">
diff --git a/addons/website_sale_options/static/src/js/website_sale.js b/addons/website_sale_options/static/src/js/website_sale.js
index 49fa7f68858..e206184b52e 100644
--- a/addons/website_sale_options/static/src/js/website_sale.js
+++ b/addons/website_sale_options/static/src/js/website_sale.js
@@ -6,10 +6,6 @@ var website = require('website.website');
var base = require('web_editor.base');
require('website_sale.website_sale');
-if(!$('.js_add_cart_variants[data-attribute_value_ids]').length) {
- return $.Deferred().reject("DOM doesn't contain '.js_add_cart_variants[data-attribute_value_ids]'");
-}
-
$('.oe_website_sale #add_to_cart, .oe_website_sale #products_grid .a-submit')
.off('click')
.removeClass('a-submit')
@@ -28,9 +24,14 @@ $('.oe_website_sale #add_to_cart, .oe_website_sale #products_grid .a-submit')
$modal.find('img:first').attr("src", "/web/image/product.product/" + product_id + "/image_medium");
+ // disable opacity on the <form> if currently active (in case the product is
+ // not published), as it interferes with bs modals
+ $form.addClass('css_options');
+
$modal.appendTo($form)
.modal()
.on('hidden.bs.modal', function () {
+ $form.removeClass('css_options'); // possibly reactivate opacity (see above)
$(this).remove();
});
diff --git a/doc/cla/corporate/camptocamp.md b/doc/cla/corporate/camptocamp.md
index 5d75bb46fa9..c934c831a83 100644
--- a/doc/cla/corporate/camptocamp.md
+++ b/doc/cla/corporate/camptocamp.md
@@ -31,3 +31,4 @@ Cyril Gaudin cyril.gaudin@camptocamp.com https://github.com/cyrilgdn
Denis Leemann denis.leemann@camptocamp.com https://github.com/leemannd
Akim Juillerat akim.juillerat@camptocamp.com https://github.com/grindtildeath
Damien Crier damien.crier@camptocamp.com https://github.com/damdam-s
+Simone Orsi simahawk@gmail.com https://github.com/simahawk
diff --git a/doc/cla/corporate/teclib.md b/doc/cla/corporate/teclib.md
index a35c0a3277d..ff9db6dd21c 100644
--- a/doc/cla/corporate/teclib.md
+++ b/doc/cla/corporate/teclib.md
@@ -11,3 +11,4 @@ Laurent Destailleur ldestailleur@teclib.com https://github.com/eldy
List of contributors:
Laurent Destailleur ldestailleur@teclib.com https://github.com/eldy
+David Halgand dhalgand@teclib.com https://github.com/halgandd
diff --git a/doc/cla/individual/halgandd.md b/doc/cla/individual/halgandd.md
new file mode 100644
index 00000000000..204d26c8ba7
--- /dev/null
+++ b/doc/cla/individual/halgandd.md
@@ -0,0 +1,11 @@
+Nantes, 2016-11-04
+
+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,
+
+David Halgand david.halgand@gmail.com https://github.com/halgandd \ No newline at end of file
diff --git a/odoo/addons/base/ir/ir_actions.py b/odoo/addons/base/ir/ir_actions.py
index 2607fd34155..000f39f1442 100644
--- a/odoo/addons/base/ir/ir_actions.py
+++ b/odoo/addons/base/ir/ir_actions.py
@@ -956,6 +956,7 @@ class IrActionsServer(models.Model):
:return: an action_id to be executed, or False is finished correctly without
return action
"""
+ res = False
for action in self:
eval_context = self._get_eval_context(action)
condition = action.condition
diff --git a/odoo/addons/test_inherit/tests/test_inherit.py b/odoo/addons/test_inherit/tests/test_inherit.py
index 4c93556a8c5..6b131d37c93 100644
--- a/odoo/addons/test_inherit/tests/test_inherit.py
+++ b/odoo/addons/test_inherit/tests/test_inherit.py
@@ -77,6 +77,12 @@ class test_inherits(common.TransactionCase):
self.assertIn(daughter, partner_demo.daughter_ids)
# search the partner from the daughter record
+ partners = self.env['res.partner'].search([('daughter_ids', 'like', 'not existing daugther')])
+ self.assertFalse(partners)
+ partners = self.env['res.partner'].search([('daughter_ids', 'not like', 'not existing daugther')])
+ self.assertIn(partner_demo, partners)
+ partners = self.env['res.partner'].search([('daughter_ids', '!=', False)])
+ self.assertIn(partner_demo, partners)
partners = self.env['res.partner'].search([('daughter_ids', 'in', daughter.ids)])
self.assertIn(partner_demo, partners)
diff --git a/odoo/osv/expression.py b/odoo/osv/expression.py
index b698ccfcb3a..da093dad323 100644
--- a/odoo/osv/expression.py
+++ b/odoo/osv/expression.py
@@ -957,7 +957,14 @@ class expression(object):
if call_null:
o2m_op = 'in' if operator in NEGATIVE_TERM_OPERATORS else 'not in'
- push(create_substitution_leaf(leaf, ('id', o2m_op, select_distinct_from_where_not_null(cr, field.inverse_name, comodel._table)), model))
+ # determine ids from field.inverse_name
+ if comodel._fields[field.inverse_name].store:
+ ids1 = select_distinct_from_where_not_null(cr, field.inverse_name, comodel._table)
+ else:
+ domain = [(field.inverse_name, '!=', False)]
+ recs = comodel.search(domain).sudo().with_context(prefetch_fields=False)
+ ids1 = recs.mapped(field.inverse_name).ids
+ push(create_substitution_leaf(leaf, ('id', o2m_op, ids1), model))
elif field.type == 'many2many':
rel_table, rel_id1, rel_id2 = field.relation, field.column1, field.column2