[IMP] sale_expense, web: hide 'search more..' from m2o dropdown

Purpose
========
In expense, we now allow any simple employee (no sales access rights) to
select sales order. Technically, the name_search is done in sudo mode,
to allow simply fetching the name_get of record.

The problem is that the m2o field widget displays the "search more"
option and click on that will open a modal executing a "search_read"
which is causing access error as the user doesn't have read access on
SO.
As we want to restrict the visible information from the SO(only name_get)
to all user, we can't hack the search_read too.

Specification
=============
To prevent user frustration, we need to remove the "search more" option.
Thus, make a private function which one have 'search more..' option
related stuff and create a new widget for sale_expense, override that
function which one is returing values as it is without any modification.

task-2057714

closes odoo/odoo#36152

Signed-off-by: Jérome Maes (jem) <jem@openerp.com>
This commit is contained in:
Hiral Bhavsar
2019-08-29 12:56:24 +00:00
parent 4242ddd7a8
commit 1d2559ce64
6 changed files with 165 additions and 35 deletions
+1
View File
@@ -16,6 +16,7 @@ This module allow to reinvoice employee expense, by setting the SO directly on t
'depends': ['sale_management', 'hr_expense'],
'data': [
'data/digest_data.xml',
'views/assets.xml',
'views/product_view.xml',
'views/hr_expense_views.xml',
'views/sale_order_views.xml',
@@ -0,0 +1,21 @@
odoo.define('sale_expense.sale_order_many2one', function (require) {
"use strict";
var FieldMany2One = require('web.relational_fields').FieldMany2One;
var FieldRegistry = require('web.field_registry');
var OrderField = FieldMany2One.extend({
/**
* hide the search more option from the dropdown menu
* @override
* @private
* @returns {Object}
*/
_manageSearchMore: function (values) {
return values;
}
});
FieldRegistry.add('sale_order_many2one', OrderField);
return OrderField;
});
@@ -0,0 +1,79 @@
odoo.define('sale_expense.field_many_to_one_tests', function (require) {
"use strict";
var FormView = require('web.FormView');
var testUtils = require('web.test_utils');
var createView = testUtils.createView;
QUnit.module('sale_expense', {
beforeEach: function () {
this.data = {
'hr.expense': {
fields: {
name: { string: "Description", type: "char" },
sale_order_id: { string: "Reinvoice Customer", type: 'many2one', relation: 'sale.order' },
},
records: []
},
'sale.order': {
fields: {
name: { string: "Name", type: "char" },
},
records: [{
id: 1,
name: "SO1",
}, {
id: 2,
name: "SO2",
}, {
id: 3,
name: "SO3"
}, {
id: 4,
name: "SO4"
}, {
id: 5,
name: "SO5"
}, {
id: 6,
name: "SO6"
}, {
id: 7,
name: "SO7"
}, {
id: 8,
name: "SO8"
}, {
id: 9,
name: "SO9"
}]
},
};
},
}, function () {
QUnit.test('sale order many2one without search more option', async function (assert) {
assert.expect(3);
var form = await createView({
View: FormView,
model: 'hr.expense',
data: this.data,
arch:
'<form string="Expense">' +
'<sheet>' +
'<group>' +
'<field name="sale_order_id" widget="sale_order_many2one"/>' +
'</group>' +
'</sheet>' +
'</form>'
});
var $dropdown = form.$('.o_field_many2one input').autocomplete('widget');
await testUtils.fields.many2one.clickOpenDropdown('sale_order_id');
assert.containsN($dropdown, 'li:not(.o_m2o_dropdown_option)', 8, 'autocomplete should contains 8 suggestions');
assert.containsN($dropdown, 'li.o_m2o_dropdown_option', 1, 'autocomplete should contain only one option');
assert.containsNone($dropdown, 'li.o_m2o_dropdown_option:contains("Search More...")', "Should not display the 'Search More... option'");
form.destroy();
});
});
});
+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="assets_backend" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<script type="text/javascript" src="/sale_expense/static/src/js/sale_order_many2one.js"/>
</xpath>
</template>
<template id="qunit_suite" inherit_id="web.qunit_suite">
<xpath expr="//script[last()]" position="after">
<script type="text/javascript" src="/sale_expense/static/tests/sale_order_many2one_tests.js"/>
</xpath>
</template>
</odoo>
@@ -7,7 +7,7 @@
<field name="priority">30</field>
<field name="arch" type="xml">
<xpath expr="//field[@name='analytic_account_id']" position="before">
<field name="sale_order_id" attrs="{'invisible': [('can_be_reinvoiced', '=', False)]}" options="{'no_create_edit': True, 'no_create': True, 'no_open': True}" context="{'sale_show_partner_name': True, 'sale_expense_all_order': True}"/>
<field name="sale_order_id" attrs="{'invisible': [('can_be_reinvoiced', '=', False)]}" options="{'no_create_edit': True, 'no_create': True, 'no_open': True}" context="{'sale_show_partner_name': True, 'sale_expense_all_order': True}" widget="sale_order_many2one"/>
<field name="can_be_reinvoiced" invisible="1"/>
</xpath>
</field>
@@ -19,6 +19,7 @@
<field name="arch" type="xml">
<xpath expr="//field[@name='sale_order_id']" position="attributes">
<attribute name="attrs">{'invisible':[['can_be_reinvoiced','=',False]],'readonly':[['state','in',['done']]]}</attribute>
<attribute name="widget">many2one</attribute>
</xpath>
</field>
<field name="groups_id" eval="[(6, 0, [ref('account.group_account_manager')])]"/>
@@ -30,6 +31,7 @@
<field name="arch" type="xml">
<xpath expr="//field[@name='sale_order_id']" position="attributes">
<attribute name="options">{'no_create_edit': True, 'no_create': True}</attribute>
<attribute name="widget">many2one</attribute>
</xpath>
</field>
<field name="groups_id" eval="[(6, 0, [ref('sales_team.group_sale_salesman')])]"/>
@@ -401,6 +401,52 @@ var FieldMany2One = AbstractField.extend({
}
};
},
/**
* @private
* @param {Object} values
* @param {string} search_val
* @param {Object} domain
* @param {Object} context
* @returns {Object}
*/
_manageSearchMore: function (values, search_val, domain, context) {
var self = this;
values = values.slice(0, this.limit);
values.push({
label: _t("Search More..."),
action: function () {
var prom;
if (search_val !== '') {
prom = self._rpc({
model: self.field.relation,
method: 'name_search',
kwargs: {
name: search_val,
args: domain,
operator: "ilike",
limit: self.SEARCH_MORE_LIMIT,
context: context,
},
});
}
Promise.resolve(prom).then(function (results) {
var dynamicFilters;
if (results) {
var ids = _.map(results, function (x) {
return x[0];
});
dynamicFilters = [{
description: _.str.sprintf(_t('Quick search: %s'), search_val),
domain: [['id', 'in', ids]],
}];
}
self._searchCreatePopup("search", false, {}, dynamicFilters);
});
},
classname: 'o_m2o_dropdown_option',
});
return values;
},
/**
* Listens to events 'field_changed' to keep track of the last event that
* has been trigerred. This allows to detect that all changes have been
@@ -552,40 +598,7 @@ var FieldMany2One = AbstractField.extend({
// search more... if more results than limit
if (values.length > self.limit) {
values = values.slice(0, self.limit);
values.push({
label: _t("Search More..."),
action: function () {
var prom;
if (search_val !== '') {
prom = self._rpc({
model: self.field.relation,
method: 'name_search',
kwargs: {
name: search_val,
args: domain,
operator: "ilike",
limit: self.SEARCH_MORE_LIMIT,
context: context,
},
});
}
Promise.resolve(prom).then(function (results) {
var dynamicFilters;
if (results) {
var ids = _.map(results, function (x) {
return x[0];
});
dynamicFilters = [{
description: _.str.sprintf(_t('Quick search: %s'), search_val),
domain: [['id', 'in', ids]],
}];
}
self._searchCreatePopup("search", false, {}, dynamicFilters);
});
},
classname: 'o_m2o_dropdown_option',
});
values = self._manageSearchMore(values, search_val, domain, context);
}
var create_enabled = self.can_create && !self.nodeOptions.no_create;
// quick create