diff options
| author | Christophe Simonis <chs@odoo.com> | 2017-01-27 18:20:28 +0100 |
|---|---|---|
| committer | Christophe Simonis <chs@odoo.com> | 2017-01-27 18:20:28 +0100 |
| commit | ba8a1c7ec24cba065934e8eb58f97d15bb60b55f (patch) | |
| tree | 40a11ec3029046caa11a9a16dd650d10c684ce5c | |
| parent | cd605442aef46d9753ca46e80d76363730d2d966 (diff) | |
| parent | e86983112eb1195e129f0c13e0a426011ebb4bf2 (diff) | |
[MERGE] forward port branch saas-11 up to e86983112eb
| -rw-r--r-- | addons/account/data/account_data.xml | 1 | ||||
| -rw-r--r-- | addons/account/models/account_invoice.py | 3 | ||||
| -rw-r--r-- | addons/account/views/account_invoice_view.xml | 2 | ||||
| -rw-r--r-- | addons/hr/models/hr.py | 4 | ||||
| -rw-r--r-- | addons/hr/models/res_partner.py | 5 | ||||
| -rw-r--r-- | addons/hr/views/hr_views.xml | 2 | ||||
| -rw-r--r-- | addons/hw_escpos/escpos/printer.py | 2 | ||||
| -rw-r--r-- | addons/l10n_be_invoice_bba/models/account_invoice.py | 2 | ||||
| -rw-r--r-- | addons/mass_mailing/wizard/test_mailing.py | 2 | ||||
| -rw-r--r-- | addons/stock/models/stock_picking.py | 22 | ||||
| -rw-r--r-- | addons/stock/tests/test_stock_flow.py | 193 | ||||
| -rw-r--r-- | addons/web/static/src/xml/base.xml | 7 | ||||
| -rw-r--r-- | addons/web/views/webclient_templates.xml | 2 | ||||
| -rw-r--r-- | addons/website_sale_options/static/src/js/website_sale.js | 9 | ||||
| -rw-r--r-- | doc/cla/corporate/camptocamp.md | 1 | ||||
| -rw-r--r-- | doc/cla/corporate/teclib.md | 1 | ||||
| -rw-r--r-- | doc/cla/individual/halgandd.md | 11 | ||||
| -rw-r--r-- | odoo/addons/base/ir/ir_actions.py | 1 | ||||
| -rw-r--r-- | odoo/addons/test_inherit/tests/test_inherit.py | 6 | ||||
| -rw-r--r-- | odoo/osv/expression.py | 9 |
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 |
