[FIX] web: Add support of button type object on One2Many KanbanRecord
Before this commit, there were a crash if you tried to add an optionnal product in a new sale order without saving it. Apply same logic as in the ListRenderer: buttons with type="object" are disabled for no saved yet records, as calling the python method with no id would make no sense. To avoid to expose this logic inside all Kanban views, we define a specific KanbanRecord Class for the One2many case. This could be refactored to prevent from duplicating this logic in list and kanban views. Original Task ID: 1945006 closes odoo/odoo#31695 Signed-off-by: Adrien Dieudonné (adr) <adr@odoo.com>
This commit is contained in:
committed by
Adrien Dieudonne
parent
7269fb32f4
commit
230ad8c381
@@ -21,6 +21,7 @@ var dialogs = require('web.view_dialogs');
|
||||
var core = require('web.core');
|
||||
var data = require('web.data');
|
||||
var Dialog = require('web.Dialog');
|
||||
var KanbanRecord = require('web.KanbanRecord');
|
||||
var KanbanRenderer = require('web.KanbanRenderer');
|
||||
var ListRenderer = require('web.ListRenderer');
|
||||
var Pager = require('web.Pager');
|
||||
@@ -1397,6 +1398,59 @@ var FieldX2Many = AbstractField.extend({
|
||||
},
|
||||
});
|
||||
|
||||
var One2ManyKanbanRecord = KanbanRecord.extend({
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Apply same logic as in the ListRenderer: buttons with type="object"
|
||||
* are disabled for no saved yet records, as calling the python method
|
||||
* with no id would make no sense.
|
||||
*
|
||||
* To avoid to expose this logic inside all Kanban views, we define
|
||||
* a specific KanbanRecord Class for the One2many case.
|
||||
*
|
||||
* This could be refactored to prevent from duplicating this logic in
|
||||
* list and kanban views.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_postProcessObjectButtons: function () {
|
||||
var self = this;
|
||||
// if the res_id is defined, it's already correctly handled by the Kanban record global event click
|
||||
if (!this.state.res_id) {
|
||||
this.$('.oe_kanban_action[data-type=object]').each(function (index, button) {
|
||||
var $button = $(button);
|
||||
if ($button.attr('warn')) {
|
||||
$button.on('click', function (e) {
|
||||
e.stopPropagation();
|
||||
self.do_warn(_t('Warning'), _t('Please click on the "save" button first.'));
|
||||
});
|
||||
} else {
|
||||
$button.attr('disabled', 'disabled');
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
* @private
|
||||
*/
|
||||
_render: function () {
|
||||
var self = this;
|
||||
return this._super.apply(this, arguments).then(function () {
|
||||
self._postProcessObjectButtons();
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
var One2ManyKanbanRenderer = KanbanRenderer.extend({
|
||||
config: _.extend({}, KanbanRenderer.prototype.config, {
|
||||
KanbanRecord: One2ManyKanbanRecord,
|
||||
}),
|
||||
});
|
||||
|
||||
var FieldOne2Many = FieldX2Many.extend({
|
||||
className: 'o_field_one2many',
|
||||
supportedFieldTypes: ['one2many'],
|
||||
@@ -1450,6 +1504,16 @@ var FieldOne2Many = FieldX2Many.extend({
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @private
|
||||
*/
|
||||
_getRenderer: function () {
|
||||
if (this.view.arch.tag === 'kanban') {
|
||||
return One2ManyKanbanRenderer;
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
/**
|
||||
* Overrides to only render the buttons if the 'create' action is available.
|
||||
*
|
||||
|
||||
@@ -4,7 +4,9 @@ odoo.define('web.field_one_to_many_tests', function (require) {
|
||||
var AbstractField = require('web.AbstractField');
|
||||
var concurrency = require('web.concurrency');
|
||||
var FormView = require('web.FormView');
|
||||
var KanbanRecord = require('web.KanbanRecord');
|
||||
var ListRenderer = require('web.ListRenderer');
|
||||
var NotificationService = require('web.NotificationService');
|
||||
var relationalFields = require('web.relational_fields');
|
||||
var testUtils = require('web.test_utils');
|
||||
var fieldUtils = require('web.field_utils');
|
||||
@@ -5418,6 +5420,145 @@ QUnit.module('fields', {}, function () {
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('one2many field with virtual ids with kanban button', function (assert) {
|
||||
assert.expect(25);
|
||||
|
||||
testUtils.mock.patch(KanbanRecord, {
|
||||
init: function () {
|
||||
this._super.apply(this, arguments);
|
||||
this._onKanbanActionClicked = this.__proto__._onKanbanActionClicked;
|
||||
},
|
||||
});
|
||||
|
||||
this.data.partner.records[0].p = [4];
|
||||
|
||||
var form = createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch: '<form>' +
|
||||
'<field name="p" mode="kanban">' +
|
||||
'<kanban>' +
|
||||
'<templates>' +
|
||||
'<field name="foo"/>' +
|
||||
'<t t-name="kanban-box">' +
|
||||
'<div>' +
|
||||
'<span><t t-esc="record.foo.value"/></span>' +
|
||||
'<button type="object" class="btn btn-link fa fa-shopping-cart" name="button_warn" string="button_warn" warn="warn" />' +
|
||||
'<button type="object" class="btn btn-link fa fa-shopping-cart" name="button_disabled" string="button_disabled" />' +
|
||||
'</div>' +
|
||||
'</t>' +
|
||||
'</templates>' +
|
||||
'</kanban>' +
|
||||
'</field>' +
|
||||
'</form>',
|
||||
archs: {
|
||||
'partner,false,form': '<form><field name="foo"/></form>',
|
||||
},
|
||||
res_id: 1,
|
||||
services: {
|
||||
notification: NotificationService.extend({
|
||||
notify: function (params) {
|
||||
assert.step(params.type);
|
||||
}
|
||||
}),
|
||||
},
|
||||
intercepts: {
|
||||
execute_action: function (event) {
|
||||
assert.step(event.data.action_data.name + '_' + event.data.env.model + '_' + event.data.env.currentID);
|
||||
event.data.on_success();
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// 1. Define all css selector
|
||||
var oKanbanView = '.o_field_widget .o_kanban_view';
|
||||
var oKanbanRecordActive = oKanbanView + ' .o_kanban_record:not(.o_kanban_ghost)';
|
||||
var oAllKanbanButton = oKanbanRecordActive + ' button[data-type="object"]';
|
||||
var btn1 = oKanbanRecordActive + ':nth-child(1) button[data-type="object"]';
|
||||
var btn2 = oKanbanRecordActive + ':nth-child(2) button[data-type="object"]';
|
||||
var btn1Warn = btn1 + '[data-name="button_warn"]';
|
||||
var btn1Disabled = btn1 + '[data-name="button_disabled"]';
|
||||
var btn2Warn = btn2 + '[data-name="button_warn"]';
|
||||
var btn2Disabled = btn2 + '[data-name="button_disabled"]';
|
||||
|
||||
// check if we already have one kanban card
|
||||
assert.containsOnce(form, oKanbanView, "should have one inner kanban view for the one2many field");
|
||||
assert.containsOnce(form, oKanbanRecordActive, "should have one kanban records yet");
|
||||
|
||||
// we have 2 buttons
|
||||
assert.containsN(form, oAllKanbanButton, 2, "should have 2 buttons type object");
|
||||
|
||||
// disabled ?
|
||||
assert.containsNone(form, oAllKanbanButton + '[disabled]', "should not have button type object disabled");
|
||||
|
||||
// click on the button
|
||||
testUtils.dom.click(form.$(btn1Disabled));
|
||||
testUtils.dom.click(form.$(btn1Warn));
|
||||
|
||||
// switch to edit mode
|
||||
testUtils.form.clickEdit(form);
|
||||
|
||||
// click on existing buttons
|
||||
testUtils.dom.click(form.$(btn1Disabled));
|
||||
testUtils.dom.click(form.$(btn1Warn));
|
||||
|
||||
// create new kanban
|
||||
testUtils.dom.click(form.$('.o_field_widget .o-kanban-button-new'));
|
||||
|
||||
// save & close the modal
|
||||
assert.strictEqual($('.modal-content input.o_field_widget').val(), 'My little Foo Value',
|
||||
"should already have the default value for field foo");
|
||||
testUtils.dom.click($('.modal-content .btn-primary').first());
|
||||
|
||||
// check new item
|
||||
assert.containsN(form, oAllKanbanButton, 4, "should have 4 buttons type object");
|
||||
assert.containsN(form, btn1, 2, "should have 2 buttons type object in area 1");
|
||||
assert.containsN(form, btn2, 2, "should have 2 buttons type object in area 2");
|
||||
assert.containsOnce(form, oAllKanbanButton + '[disabled]', "should have 1 button type object disabled");
|
||||
|
||||
assert.strictEqual(form.$(btn2Disabled).attr('disabled'), 'disabled', 'Should have a button type object disabled in area 2');
|
||||
assert.strictEqual(form.$(btn2Warn).attr('disabled'), undefined, 'Should have a button type object not disabled in area 2');
|
||||
assert.strictEqual(form.$(btn2Warn).attr('warn'), 'warn', 'Should have a button type object with warn attr in area 2');
|
||||
|
||||
// click all buttons
|
||||
testUtils.dom.click(form.$(btn1Disabled));
|
||||
testUtils.dom.click(form.$(btn1Warn));
|
||||
testUtils.dom.click(form.$(btn2Disabled));
|
||||
testUtils.dom.click(form.$(btn2Warn));
|
||||
|
||||
// save the form
|
||||
testUtils.form.clickSave(form);
|
||||
|
||||
assert.containsNone(form, oAllKanbanButton + '[disabled]', "should not have button type object disabled after save");
|
||||
|
||||
// click all buttons
|
||||
testUtils.dom.click(form.$(btn1Disabled));
|
||||
testUtils.dom.click(form.$(btn1Warn));
|
||||
testUtils.dom.click(form.$(btn2Disabled));
|
||||
testUtils.dom.click(form.$(btn2Warn));
|
||||
|
||||
assert.verifySteps([
|
||||
"button_disabled_partner_4",
|
||||
"button_warn_partner_4",
|
||||
|
||||
"button_disabled_partner_4",
|
||||
"button_warn_partner_4",
|
||||
|
||||
"button_disabled_partner_4",
|
||||
"button_warn_partner_4",
|
||||
"warning", // warn btn8
|
||||
|
||||
"button_disabled_partner_4",
|
||||
"button_warn_partner_4",
|
||||
"button_disabled_partner_8",
|
||||
"button_warn_partner_8"
|
||||
], "should have triggered theses 11 clicks event");
|
||||
|
||||
testUtils.mock.unpatch(KanbanRecord);
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('focusing fields in one2many list', function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user