diff options
| author | Christophe Simonis <chs@odoo.com> | 2018-01-22 17:47:32 +0100 |
|---|---|---|
| committer | Christophe Simonis <chs@odoo.com> | 2018-01-22 17:47:32 +0100 |
| commit | fa1f2132e79f2ac2314a8b417d5d35d6571bd41b (patch) | |
| tree | 3789a8335dd5089998de3c5c6a391b2d95a85187 /addons/web | |
| parent | db27489fe756b24647e993a22340705625839698 (diff) | |
| parent | 950d9b2369046f2e3199d144df8edbd0e63f8a2f (diff) | |
[MERGE] forward port branch 11.0 up to 950d9b23690
Diffstat (limited to 'addons/web')
| -rw-r--r-- | addons/web/static/src/js/fields/abstract_field.js | 5 | ||||
| -rw-r--r-- | addons/web/static/src/js/fields/basic_fields.js | 1 | ||||
| -rw-r--r-- | addons/web/static/src/js/fields/field_utils.js | 5 | ||||
| -rw-r--r-- | addons/web/static/src/js/services/data_manager.js | 4 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/basic/basic_model.js | 23 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/kanban/kanban_column.js | 7 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/kanban/kanban_controller.js | 8 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/kanban/kanban_model.js | 16 | ||||
| -rw-r--r-- | addons/web/static/tests/fields/relational_fields_tests.js | 69 | ||||
| -rw-r--r-- | addons/web/static/tests/views/basic_model_tests.js | 1 | ||||
| -rw-r--r-- | addons/web/static/tests/views/form_tests.js | 4 | ||||
| -rw-r--r-- | addons/web/static/tests/views/kanban_tests.js | 147 | ||||
| -rw-r--r-- | addons/web/tests/test_js.py | 2 |
13 files changed, 256 insertions, 36 deletions
diff --git a/addons/web/static/src/js/fields/abstract_field.js b/addons/web/static/src/js/fields/abstract_field.js index 8bb9afca065..2534f8ae90a 100644 --- a/addons/web/static/src/js/fields/abstract_field.js +++ b/addons/web/static/src/js/fields/abstract_field.js @@ -45,11 +45,6 @@ var AbstractField = Widget.extend({ }, /** - * fields can extend the context, e.g. binary fields add {bin_size: true} - */ - context: null, - - /** * An object representing fields to be fetched by the model eventhough not present in the view * This object contains "field name" as key and an object as value. * That value object must contain the key "type" diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js index f3e66cee421..94facd80ceb 100644 --- a/addons/web/static/src/js/fields/basic_fields.js +++ b/addons/web/static/src/js/fields/basic_fields.js @@ -1094,7 +1094,6 @@ var AbstractFieldBinary = AbstractField.extend({ }, 'click .o_clear_file_button': 'on_clear', }), - context: { bin_size: true }, init: function (parent, name, record) { this._super.apply(this, arguments); this.fields = record.fields; diff --git a/addons/web/static/src/js/fields/field_utils.js b/addons/web/static/src/js/fields/field_utils.js index 5ac180b1de1..5a11f1b0ba8 100644 --- a/addons/web/static/src/js/fields/field_utils.js +++ b/addons/web/static/src/js/fields/field_utils.js @@ -30,7 +30,7 @@ var _t = core._t; /** * Convert binary to bin_size * - * @param {string} value base64 representation of the binary (might be already a bin_size!) + * @param {string} [value] base64 representation of the binary (might be already a bin_size!) * @param {Object} [field] * a description of the field (note: this parameter is ignored) * @param {Object} [options] additional options (note: this parameter is ignored) @@ -38,6 +38,9 @@ var _t = core._t; * @returns {string} bin_size (which is human-readable) */ function formatBinary(value, field, options) { + if (!value) { + return ''; + } return utils.binaryToBinsize(value); } diff --git a/addons/web/static/src/js/services/data_manager.js b/addons/web/static/src/js/services/data_manager.js index 948d3a1c73a..72ec816efdd 100644 --- a/addons/web/static/src/js/services/data_manager.js +++ b/addons/web/static/src/js/services/data_manager.js @@ -387,10 +387,6 @@ return core.Class.extend({ attrs.fieldDependencies = attrs.Widget.prototype.fieldDependencies; } - if (attrs.Widget.prototype.context) { - attrs.context = attrs.Widget.prototype.context; - } - return attrs; }, /** diff --git a/addons/web/static/src/js/views/basic/basic_model.js b/addons/web/static/src/js/views/basic/basic_model.js index a6979776c99..5296c6db7d5 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -1854,7 +1854,7 @@ var BasicModel = AbstractModel.extend({ model: record.model, method: 'read', args: [[record.res_id], fieldNames], - context: record.getContext(), + context: _.extend({}, record.getContext(), {bin_size: true}), }) .then(function (result) { if (result.length === 0) { @@ -2374,7 +2374,7 @@ var BasicModel = AbstractModel.extend({ }); record.data[fieldName] = list.id; if (!fieldInfo.__no_fetch) { - var def = self._readUngroupedList(list).then(function (list) { + var def = self._readUngroupedList(list).then(function () { return $.when( self._fetchX2ManysBatched(list), self._fetchReferencesBatched(list) @@ -2403,6 +2403,7 @@ var BasicModel = AbstractModel.extend({ var fields = view ? view.fields : fieldInfo.relatedFields; var viewType = view ? view.type : fieldInfo.viewType; list = this._applyX2ManyOperations(list); + this._sortList(list); var x2mRecords = []; // step 1: collect ids @@ -2640,7 +2641,7 @@ var BasicModel = AbstractModel.extend({ var relRecordAdded = []; var relRecordUpdated = []; _.each(list._changes, function (change) { - if (change.operation === 'ADD') { + if (change.operation === 'ADD' && change.id) { relRecordAdded.push(self.localData[change.id]); } else if (change.operation === 'UPDATE' && !self.isNew(change.id)) { // ignore new records that would have been updated @@ -2699,6 +2700,10 @@ var BasicModel = AbstractModel.extend({ } else if (_.contains(addedIds, list.res_ids[i])) { // this is a new id (maybe existing in DB, but new in JS) relRecord = _.findWhere(relRecordAdded, {res_id: list.res_ids[i]}); + if (!relRecord) { + commands[fieldName].push(x2ManyCommands.link_to(list.res_ids[i])); + continue; + } changes = this._generateChanges(relRecord, options); if (changes.id) { // the subrecord already exists in db @@ -3092,23 +3097,13 @@ var BasicModel = AbstractModel.extend({ id: {type: 'integer'}, }, params.fields); - // datapoint context is extended by fields widgets - var context = _.extend({}, params.context); - if (params.fieldsInfo && params.viewType) { - _.each(params.fieldsInfo[params.viewType], function (field) { - if (field.context) { - _.extend(context, field.context); - } - }); - } - var dataPoint = { _cache: type === 'list' ? {} : undefined, _changes: null, _domains: {}, _rawChanges: {}, aggregateValues: params.aggregateValues || {}, - context: context, + context: params.context, count: params.count || res_ids.length, data: data, domain: params.domain || [], diff --git a/addons/web/static/src/js/views/kanban/kanban_column.js b/addons/web/static/src/js/views/kanban/kanban_column.js index 0e50e37194f..d3553d8db12 100644 --- a/addons/web/static/src/js/views/kanban/kanban_column.js +++ b/addons/web/static/src/js/views/kanban/kanban_column.js @@ -167,6 +167,13 @@ var KanbanColumn = Widget.extend({ * Adds the quick create record to the top of the column. */ addQuickCreate: function () { + if (this.folded) { + // first open the column, and then add the quick create + this.trigger_up('column_toggle_fold', { + openQuickCreate: true, + }); + return; + } if (this.quickCreateWidget) { return; } diff --git a/addons/web/static/src/js/views/kanban/kanban_controller.js b/addons/web/static/src/js/views/kanban/kanban_controller.js index 9e97ec40686..26f379d4a39 100644 --- a/addons/web/static/src/js/views/kanban/kanban_controller.js +++ b/addons/web/static/src/js/views/kanban/kanban_controller.js @@ -207,6 +207,7 @@ var KanbanController = BasicController.extend({ * @param {OdooEvent} event */ _onButtonClicked: function (event) { + event.stopPropagation(); var self = this; var attrs = event.data.attrs; var record = event.data.record; @@ -388,13 +389,18 @@ var KanbanController = BasicController.extend({ /** * @private * @param {OdooEvent} event + * @param {boolean} [event.data.openQuickCreate=false] if true, opens the + * QuickCreate in the toggled column (it assumes that we are opening it) */ _onToggleColumn: function (event) { var self = this; var column = event.target; this.model.toggleGroup(column.db_id).then(function (db_id) { var data = self.model.get(db_id); - self.renderer.updateColumn(db_id, data); + var options = { + openQuickCreate: !!event.data.openQuickCreate, + }; + self.renderer.updateColumn(db_id, data, options); self._updateEnv(); }); }, diff --git a/addons/web/static/src/js/views/kanban/kanban_model.js b/addons/web/static/src/js/views/kanban/kanban_model.js index aba59555e93..df6172f0d8b 100644 --- a/addons/web/static/src/js/views/kanban/kanban_model.js +++ b/addons/web/static/src/js/views/kanban/kanban_model.js @@ -22,6 +22,7 @@ var KanbanModel = BasicModel.extend({ * @returns {Deferred<string>} resolves to the local id of the new record */ addRecordToGroup: function (groupID, resId) { + var self = this; var group = this.localData[groupID]; var new_record = this._makeDataPoint({ res_id: resId, @@ -31,15 +32,16 @@ var KanbanModel = BasicModel.extend({ viewType: group.viewType, parentID: groupID, }); - group.data.unshift(new_record.id); - group.res_ids.unshift(resId); - group.count++; - - // update the res_ids and count of the parent - this.localData[group.parentID].count++; - this._updateParentResIDs(group); return this._fetchRecord(new_record).then(function (result) { + group.data.unshift(new_record.id); + group.res_ids.unshift(resId); + group.count++; + + // update the res_ids and count of the parent + self.localData[group.parentID].count++; + self._updateParentResIDs(group); + return result.id; }); }, diff --git a/addons/web/static/tests/fields/relational_fields_tests.js b/addons/web/static/tests/fields/relational_fields_tests.js index ef5139511fe..ff9a9c63621 100644 --- a/addons/web/static/tests/fields/relational_fields_tests.js +++ b/addons/web/static/tests/fields/relational_fields_tests.js @@ -6916,6 +6916,7 @@ QUnit.module('relational_fields', { if (args.method === 'read' && args.model === 'partner') { assert.deepEqual(args.kwargs.context, { active_field: 2, + bin_size: true, someKey: 'some value', }, "sent context should be correct"); } @@ -7639,6 +7640,74 @@ QUnit.module('relational_fields', { form.destroy(); }); + QUnit.test('one2many with multiple pages and sequence field, part2', function (assert) { + assert.expect(1); + + this.data.partner.records[0].turtles = [3, 2, 1]; + this.data.partner.onchanges.turtles = function () {}; + + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch:'<form string="Partners">' + + '<field name="turtles">' + + '<tree limit="2">' + + '<field name="turtle_int" widget="handle"/>' + + '<field name="turtle_foo"/>' + + '<field name="partner_ids" invisible="1"/>' + + '</tree>' + + '</field>' + + '</form>', + res_id: 1, + mockRPC: function (route, args) { + if (args.method === 'onchange') { + return $.when({value: { turtles: [ + [5], + [1, 1, {turtle_foo: "from onchange id2", partner_ids: [[5]]}], + [1, 3, {turtle_foo: "from onchange id3", partner_ids: [[5]]}], + ]}}); + } + return this._super(route, args); + }, + viewOptions: { + mode: 'edit', + }, + }); + form.$('span[name="delete"]').first().click(); + assert.strictEqual(form.$('.o_data_row').text(), 'from onchange id2from onchange id3', + 'onchange has been properly applied'); + form.destroy(); + }); + + QUnit.test('new record, with one2many with more default values than limit', function (assert) { + assert.expect(2); + + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch:'<form string="Partners">' + + '<field name="turtles">' + + '<tree limit="2">' + + '<field name="turtle_foo"/>' + + '</tree>' + + '</field>' + + '</form>', + context: { default_turtles: [1,2,3]}, + viewOptions: { + mode: 'edit', + }, + }); + assert.strictEqual(form.$('.o_data_row').text(), 'yopblip', + 'data has been properly loaded'); + form.$buttons.find('.o_form_button_save').click(); + + assert.strictEqual(form.$('.o_data_row').text(), 'yopblip', + 'data has been properly saved'); + form.destroy(); + }); + QUnit.module('FieldMany2Many'); QUnit.test('many2many kanban: edition', function (assert) { diff --git a/addons/web/static/tests/views/basic_model_tests.js b/addons/web/static/tests/views/basic_model_tests.js index 72061f326e0..492dfd4224c 100644 --- a/addons/web/static/tests/views/basic_model_tests.js +++ b/addons/web/static/tests/views/basic_model_tests.js @@ -74,6 +74,7 @@ QUnit.module('Views', { mockRPC: function (route, args) { assert.deepEqual(args.kwargs.context, { active_field: 2, + bin_size: true, someKey: 'some value', }, "should have sent the correct context"); return this._super.apply(this, arguments); diff --git a/addons/web/static/tests/views/form_tests.js b/addons/web/static/tests/views/form_tests.js index 9f2a9b68698..9e7c89628c4 100644 --- a/addons/web/static/tests/views/form_tests.js +++ b/addons/web/static/tests/views/form_tests.js @@ -199,7 +199,7 @@ QUnit.module('Views', { }); QUnit.test('only necessary fields are fetched with correct context', function (assert) { - assert.expect(1); + assert.expect(2); var form = createView({ View: FormView, @@ -214,6 +214,8 @@ QUnit.module('Views', { // field, not sure why. Maybe this test should be modified. assert.deepEqual(args.args[1], ["foo", "display_name"], "should only fetch requested fields"); + assert.deepEqual(args.kwargs.context, {bin_size: true}, + "bin_size should always be in the context"); return this._super(route, args); } }); diff --git a/addons/web/static/tests/views/kanban_tests.js b/addons/web/static/tests/views/kanban_tests.js index 9e6d6e9e30f..250bb8ef64f 100644 --- a/addons/web/static/tests/views/kanban_tests.js +++ b/addons/web/static/tests/views/kanban_tests.js @@ -33,9 +33,10 @@ QUnit.module('Views', { state: { string: "State", type: "selection", selection: [["abc", "ABC"], ["def", "DEF"], ["ghi", "GHI"]]}, date: {string: "Date Field", type: 'date'}, datetime: {string: "Datetime Field", type: 'datetime'}, + image: {string: "Image", type: "binary"}, }, records: [ - {id: 1, bar: true, foo: "yop", int_field: 10, qux: 0.4, product_id: 3, state: "abc", category_ids: []}, + {id: 1, bar: true, foo: "yop", int_field: 10, qux: 0.4, product_id: 3, state: "abc", category_ids: [], 'image': 'R0lGODlhAQABAAD/ACwAAAAAAQABAAACAA=='}, {id: 2, bar: true, foo: "blip", int_field: 9, qux: 13, product_id: 5, state: "def", category_ids: [6]}, {id: 3, bar: true, foo: "gnap", int_field: 17, qux: -3, product_id: 3, state: "ghi", category_ids: [7]}, {id: 4, bar: false, foo: "blip", int_field: -4, qux: 9, product_id: 5, state: "ghi", category_ids: []}, @@ -440,6 +441,50 @@ QUnit.module('Views', { kanban.destroy(); }); + QUnit.test('quick create when first column is folded', function (assert) { + assert.expect(6); + + var kanban = createView({ + View: KanbanView, + model: 'partner', + data: this.data, + arch: '<kanban on_create="quick_create">' + + '<field name="bar"/>' + + '<templates><t t-name="kanban-box">' + + '<div><field name="foo"/></div>' + + '</t></templates></kanban>', + groupBy: ['bar'], + }); + + assert.notOk(kanban.$('.o_kanban_group:first').hasClass('o_column_folded'), + "first column should not be folded"); + + // fold the first column + kanban.$('.o_kanban_group:first .o_kanban_toggle_fold').click(); + + assert.ok(kanban.$('.o_kanban_group:first').hasClass('o_column_folded'), + "first column should be folded"); + + // click on 'Create' to open the quick create in the first column + kanban.$buttons.find('.o-kanban-button-new').click(); + + assert.notOk(kanban.$('.o_kanban_group:first').hasClass('o_column_folded'), + "first column should no longer be folded"); + var $quickCreate = kanban.$('.o_kanban_group:first .o_kanban_quick_create'); + assert.strictEqual($quickCreate.length, 1, + "should have added a quick create element in first column"); + + // fold again the first column + kanban.$('.o_kanban_group:first .o_kanban_toggle_fold').click(); + + assert.ok(kanban.$('.o_kanban_group:first').hasClass('o_column_folded'), + "first column should be folded"); + assert.strictEqual(kanban.$('.o_kanban_quick_create').length, 0, + "there should be no more quick create"); + + kanban.destroy(); + }); + QUnit.test('quick create and edit in grouped mode', function (assert) { assert.expect(6); @@ -545,6 +590,80 @@ QUnit.module('Views', { kanban.destroy(); }); + QUnit.test('quick create several records on slow network', function (assert) { + assert.expect(6); + + var self = this; + var def; + var kanban = createView({ + View: KanbanView, + model: 'partner', + data: this.data, + arch: '<kanban class="o_kanban_test" on_create="quick_create">' + + '<field name="bar"/>' + + '<templates><t t-name="kanban-box">' + + '<div><field name="foo"/></div>' + + '</t></templates></kanban>', + groupBy: ['bar'], + mockRPC: function (route, args) { + var result = this._super.apply(this, arguments); + if (args.method === 'name_create') { + return result.then(function (result) { + // simulate a dynamic default value for foo based on display_name, + // so that we can check that the record has been fetched before + // being displayed + var newRec = _.findWhere(self.data.partner.records, {id: result[0]}); + newRec.foo = newRec.display_name; + return result; + }); + } + if (args.method === 'read') { + return $.when(def).then(_.constant(result)); + } + return result; + }, + }); + + assert.strictEqual(kanban.$('.o_kanban_group:first .o_kanban_record').length, 1, + "first column should contain one record"); + + // click to add a record, and add two in a row (first one will be delayed) + kanban.$('.o_kanban_header .o_kanban_quick_add i').first().click(); + + assert.strictEqual(kanban.$('.o_kanban_quick_create').length, 1, + "the quick create should be open"); + + def = $.Deferred(); + kanban.$('.o_kanban_quick_create input') + .val('new partner 1') + .trigger($.Event('keypress', { + which: $.ui.keyCode.ENTER, + keyCode: $.ui.keyCode.ENTER, + })); + var firstDef = def; + def = null; + kanban.$('.o_kanban_quick_create input') + .val('new partner 2') + .trigger($.Event('keypress', { + which: $.ui.keyCode.ENTER, + keyCode: $.ui.keyCode.ENTER, + })); + + assert.strictEqual(kanban.$('.o_kanban_group:first .o_kanban_record').length, 2, + "first column should now contain two records"); + assert.strictEqual(kanban.$('.o_kanban_group:first .o_kanban_record:first').text(), + 'new partner 2', "new record should have been fetched"); + + firstDef.resolve(); + + assert.strictEqual(kanban.$('.o_kanban_group:first .o_kanban_record').length, 3, + "first column should now contain three records"); + assert.strictEqual(kanban.$('.o_kanban_group:first .o_kanban_record:first').text(), + 'new partner 1', "new record should have been fetched"); + + kanban.destroy(); + }); + QUnit.test('quick create fail in grouped', function (assert) { assert.expect(7); @@ -2339,6 +2458,32 @@ QUnit.module('Views', { "quick create should have been added in the first column"); } }); + + QUnit.test('test displaying image', function (assert) { + assert.expect(2); + + var kanban = createView({ + View: KanbanView, + model: 'partner', + data: this.data, + arch: '<kanban class="o_kanban_test">' + + '<field name="id"/>' + + '<field name="image"/>' + + '<templates><t t-name="kanban-box"><div>' + + '<img t-att-src="kanban_image(\'partner\', \'image\', record.id.raw_value)"/>' + + '</div></t></templates>' + + '</kanban>', + }); + + var imageOnRecord = kanban.$('img[src*="/web/image"][src*="&id=1"]'); + assert.strictEqual(imageOnRecord.length, 1, "partner with image display image"); + + var placeholders = kanban.$('img[src$="/web/static/src/img/placeholder.png"]'); + assert.strictEqual(placeholders.length, this.data.partner.records.length - 1, + "partner with image should displaiy"); + + kanban.destroy(); + }); }); }); diff --git a/addons/web/tests/test_js.py b/addons/web/tests/test_js.py index 49f5b8d6dce..ab0eefb360b 100644 --- a/addons/web/tests/test_js.py +++ b/addons/web/tests/test_js.py @@ -13,7 +13,7 @@ class WebSuite(odoo.tests.HttpCase): def test_01_js(self): # webclient desktop test suite - self.phantom_js('/web/tests?mod=web', "", "", login='admin', timeout=300) + self.phantom_js('/web/tests?mod=web', "", "", login='admin', timeout=360) def test_02_js(self): # webclient mobile test suite |
