summaryrefslogtreecommitdiff
path: root/addons/web
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2018-01-22 17:47:32 +0100
committerChristophe Simonis <chs@odoo.com>2018-01-22 17:47:32 +0100
commitfa1f2132e79f2ac2314a8b417d5d35d6571bd41b (patch)
tree3789a8335dd5089998de3c5c6a391b2d95a85187 /addons/web
parentdb27489fe756b24647e993a22340705625839698 (diff)
parent950d9b2369046f2e3199d144df8edbd0e63f8a2f (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.js5
-rw-r--r--addons/web/static/src/js/fields/basic_fields.js1
-rw-r--r--addons/web/static/src/js/fields/field_utils.js5
-rw-r--r--addons/web/static/src/js/services/data_manager.js4
-rw-r--r--addons/web/static/src/js/views/basic/basic_model.js23
-rw-r--r--addons/web/static/src/js/views/kanban/kanban_column.js7
-rw-r--r--addons/web/static/src/js/views/kanban/kanban_controller.js8
-rw-r--r--addons/web/static/src/js/views/kanban/kanban_model.js16
-rw-r--r--addons/web/static/tests/fields/relational_fields_tests.js69
-rw-r--r--addons/web/static/tests/views/basic_model_tests.js1
-rw-r--r--addons/web/static/tests/views/form_tests.js4
-rw-r--r--addons/web/static/tests/views/kanban_tests.js147
-rw-r--r--addons/web/tests/test_js.py2
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