[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:
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user