summaryrefslogtreecommitdiff
path: root/addons/web
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2018-11-22 20:06:48 +0100
committerChristophe Simonis <chs@odoo.com>2018-11-22 20:06:48 +0100
commitb4e1be8ab07d883637a7f0f00c37d9bbf5ef6ee8 (patch)
tree591d7ce8d61d9a9e5d35a695f6a2276066f1a4a3 /addons/web
parent1c4d49330e7f9719e372040f0c8729d3cf3d09fa (diff)
parentb09f624f20781d65baf2060df40383898e6f86e5 (diff)
[MERGE] forward port branch saas-11.3 up to b09f624f207
Diffstat (limited to 'addons/web')
-rw-r--r--addons/web/static/src/js/views/basic/basic_model.js13
-rw-r--r--addons/web/static/src/js/views/list/list_controller.js9
-rw-r--r--addons/web/static/src/js/views/search/favorites_menu.js23
-rw-r--r--addons/web/static/tests/fields/relational_fields_tests.js89
-rw-r--r--addons/web/static/tests/views/list_tests.js93
5 files changed, 219 insertions, 8 deletions
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 f87106ec0c2..b31106b151e 100644
--- a/addons/web/static/src/js/views/basic/basic_model.js
+++ b/addons/web/static/src/js/views/basic/basic_model.js
@@ -1596,6 +1596,11 @@ var BasicModel = AbstractModel.extend({
record._rawChanges[name] = val;
return;
}
+ if (record._rawChanges[name]) {
+ // if previous _rawChanges exists, clear them since the field is now knwon
+ // and restoring outdated onchange over posterious change is wrong
+ delete record._rawChanges[name];
+ }
var oldValue = name in record._changes ? record._changes[name] : record.data[name];
var id;
if (field.type === 'many2one') {
@@ -2733,11 +2738,6 @@ var BasicModel = AbstractModel.extend({
relationField: field.relation_field,
viewType: view ? view.type : fieldInfo.viewType,
});
- // set existing changes to the list
- if (record._changes && record._changes[fieldName]) {
- list._changes = self.localData[record._changes[fieldName]]._changes;
- record._changes[fieldName] = list.id;
- }
record.data[fieldName] = list.id;
if (!fieldInfo.__no_fetch) {
var def = self._readUngroupedList(list).then(function () {
@@ -3528,7 +3528,7 @@ var BasicModel = AbstractModel.extend({
model: params.modelName,
offset: params.offset || (type === 'record' ? _.indexOf(res_ids, res_id) : 0),
openGroupByDefault: params.openGroupByDefault,
- orderedBy: params.orderedBy || [],
+ orderedBy: params.orderedBy || (params.context && params.context.orderedBy) || [],
orderedResIDs: params.orderedResIDs,
parentID: params.parentID,
rawContext: params.rawContext,
@@ -4230,6 +4230,7 @@ var BasicModel = AbstractModel.extend({
if (options.context !== undefined) {
element.context = options.context;
+ element.orderedBy = options.context.orderedBy || element.orderedBy;
}
if (options.domain !== undefined) {
element.domain = options.domain;
diff --git a/addons/web/static/src/js/views/list/list_controller.js b/addons/web/static/src/js/views/list/list_controller.js
index 3691903c079..955ef2ade39 100644
--- a/addons/web/static/src/js/views/list/list_controller.js
+++ b/addons/web/static/src/js/views/list/list_controller.js
@@ -79,6 +79,14 @@ var ListController = BasicController.extend({
return $.Deferred().resolve();
}
},
+ /*
+ * @override
+ */
+ getContext: function () {
+ var context = this._super.apply(this, arguments);
+ context.orderedBy = this.model.get(this.handle, {raw: true}).orderedBy || [];
+ return context;
+ },
/**
* Returns the list of currently selected res_ids (with the check boxes on
* the left)
@@ -199,6 +207,7 @@ var ListController = BasicController.extend({
} else {
this.selectedRecords = [];
}
+
params.selectedRecords = this.selectedRecords;
return this._super.apply(this, arguments);
},
diff --git a/addons/web/static/src/js/views/search/favorites_menu.js b/addons/web/static/src/js/views/search/favorites_menu.js
index a40f38d4095..46949e901a3 100644
--- a/addons/web/static/src/js/views/search/favorites_menu.js
+++ b/addons/web/static/src/js/views/search/favorites_menu.js
@@ -103,9 +103,17 @@ return Widget.extend({
var controllerContext;
this.trigger_up('get_controller_context', {
callback: function (ctx) {
- controllerContext = ctx;
+ if (ctx && ctx.orderedBy) {
+ var sort = _.map(ctx.orderedBy, function (obj) {
+ var order = obj.asc ? 'ASC' : 'DESC';
+ return obj.name + ' ' + order;
+ });
+ self.searchview.dataset.set_sort(sort);
+ }
+ controllerContext = _.omit(ctx, ['orderedBy']);
},
});
+
var results = pyUtils.eval_domains_and_contexts({
domains: [],
contexts: [user_context].concat(search.contexts.concat(controllerContext || [])),
@@ -186,7 +194,18 @@ return Widget.extend({
category: _t("Custom Filter"),
icon: 'fa-star',
field: {
- get_context: function () { return filter.context; },
+ get_context: function () {
+ var sortParsed = JSON.parse(filter.sort || "[]");
+ var orderedBy = [];
+ _.each(sortParsed, function (sort) {
+ orderedBy.push({
+ name: sort[0] === '-' ? sort.slice(1) : sort,
+ asc: sort[0] === '-' ? false : true,
+ });
+ });
+
+ return _.defaults({}, filter.context, {orderedBy : orderedBy});
+ },
get_groupby: function () { return [filter.context]; },
// facet is not used
get_domain: function (facet, noEvaluation) {
diff --git a/addons/web/static/tests/fields/relational_fields_tests.js b/addons/web/static/tests/fields/relational_fields_tests.js
index 9c7c6363a40..52f181a24ec 100644
--- a/addons/web/static/tests/fields/relational_fields_tests.js
+++ b/addons/web/static/tests/fields/relational_fields_tests.js
@@ -10346,6 +10346,95 @@ QUnit.module('relational_fields', {
form.destroy();
});
+ QUnit.test('onchange on unloaded record clearing posterious change', function (assert) {
+ // when we got onchange result for fields of record that were not
+ // already available because they were in a inline view not already
+ // opened, in a given configuration the change were applied ignoring
+ // posteriously changed data, thus an added/removed/modified line could
+ // be reset to the original onchange data
+ assert.expect(5);
+
+ var numUserOnchange = 0;
+
+ this.data.user.onchanges = {
+ partner_ids: function (obj) {
+ // simulate actual server onchange after save of modal with new record
+ if (numUserOnchange === 0) {
+ obj.partner_ids = _.clone(obj.partner_ids);
+ obj.partner_ids.unshift([5]);
+ obj.partner_ids[1][2].turtles.unshift([5]);
+ obj.partner_ids[2] = [1, 2, {
+ display_name: 'second record',
+ trululu: 1,
+ turtles: [[5]],
+ }];
+ } else if (numUserOnchange === 1) {
+ obj.partner_ids = _.clone(obj.partner_ids);
+ obj.partner_ids.unshift([5]);
+ obj.partner_ids[1][2].turtles.unshift([5]);
+ obj.partner_ids[2][2].turtles.unshift([5]);
+ }
+ numUserOnchange++;
+ },
+ };
+
+ var form = createView({
+ View: FormView,
+ model: 'user',
+ data: this.data,
+ arch: '<form><sheet><group>' +
+ '<field name="partner_ids">' +
+ '<form>'+
+ '<field name="trululu"/>' +
+ '<field name="turtles">' +
+ '<tree editable="bottom">' +
+ '<field name="display_name"/>' +
+ '</tree>' +
+ '</field>' +
+ '</form>' +
+ '<tree>' +
+ '<field name="display_name"/>' +
+ '</tree>' +
+ '</field>' +
+ '</group></sheet></form>',
+ res_id: 17,
+ });
+
+ // open first partner and change turtle name
+ form.$buttons.find('.o_form_button_edit').click();
+ form.$('.o_data_row:eq(0)').click();
+ $('.modal .o_data_cell:eq(0)').click();
+ $('.modal input[name="display_name"]').val('Donatello').change();
+ $('.modal .btn-primary').click();
+
+ // open second partner and add a turtle
+ form.$('.o_data_row:eq(1)').click();
+ $('.modal .o_field_x2many_list_row_add a').click();
+ $('.modal input[name="display_name"]').val('Michelangelo').change();
+ $('.modal .btn-primary').click();
+
+ assert.strictEqual(numUserOnchange, 2,
+ 'there should 2 and only 2 onchange from closing the partner modal');
+
+ // check first record still has change
+ form.$('.o_data_row:eq(0)').click();
+ assert.strictEqual($('.modal .o_data_row').length, 1,
+ 'only 1 turtle for first partner');
+ assert.strictEqual($('.modal .o_data_row').text(), 'Donatello',
+ 'first partner turtle is Donatello');
+ $('.modal .o_form_button_cancel').click();
+
+ // check second record still has changes
+ form.$('.o_data_row:eq(1)').click();
+ assert.strictEqual($('.modal .o_data_row').length, 1,
+ 'only 1 turtle for second partner');
+ assert.strictEqual($('.modal .o_data_row').text(), 'Michelangelo',
+ 'second partner turtle is Michelangelo');
+ $('.modal .o_form_button_cancel').click();
+
+ form.destroy();
+ });
+
QUnit.test('click on URL should not open the record', function (assert) {
assert.expect(2);
diff --git a/addons/web/static/tests/views/list_tests.js b/addons/web/static/tests/views/list_tests.js
index 030a676f4eb..76f4d3f6808 100644
--- a/addons/web/static/tests/views/list_tests.js
+++ b/addons/web/static/tests/views/list_tests.js
@@ -378,6 +378,99 @@ QUnit.module('Views', {
list.destroy();
});
+ QUnit.test('ordered list, sort attribute in context', function (assert) {
+ assert.expect(1);
+ // Equivalent to saving a custom filter
+
+ this.data.foo.fields.foo.sortable = true;
+ this.data.foo.fields.date.sortable = true;
+
+ var list = createView({
+ View: ListView,
+ model: 'foo',
+ data: this.data,
+ arch: '<tree>' +
+ '<field name="foo"/>' +
+ '<field name="date"/>' +
+ '</tree>',
+ });
+
+ // Descending order on Foo
+ list.$('th.o_column_sortable:contains("Foo")').click();
+ list.$('th.o_column_sortable:contains("Foo")').click();
+
+ // Ascending order on Date
+ list.$('th.o_column_sortable:contains("Date")').click();
+
+ var listContext = list.getContext();
+ assert.deepEqual(listContext,
+ {
+ orderedBy: [{
+ name: 'date',
+ asc: true,
+ }, {
+ name: 'foo',
+ asc: false,
+ }]
+ }, 'the list should have the right orderedBy in context');
+ list.destroy();
+ });
+
+ QUnit.test('Loading a filter with a sort attribute', function (assert) {
+ assert.expect(2);
+
+ this.data.foo.fields.foo.sortable = true;
+ this.data.foo.fields.date.sortable = true;
+
+ var searchReads = 0;
+ var list = createView({
+ View: ListView,
+ model: 'foo',
+ data: this.data,
+ arch: '<tree>' +
+ '<field name="foo"/>' +
+ '<field name="date"/>' +
+ '</tree>',
+ context: {
+ orderedBy: [{
+ name: 'date',
+ asc: true,
+ }, {
+ name: 'foo',
+ asc: false,
+ }]
+ },
+ mockRPC: function (route, args) {
+ if (route === '/web/dataset/search_read') {
+ if (searchReads === 0) {
+ assert.strictEqual(args.sort, 'date ASC, foo DESC',
+ 'The sort attribute of the filter should be used by the initial search_read');
+ } else if (searchReads === 1) {
+ assert.strictEqual(args.sort, 'date DESC, foo ASC',
+ 'The sort attribute of the filter should be used by the next search_read');
+ }
+ searchReads += 1;
+ }
+ return this._super.apply(this,arguments);
+ },
+ });
+
+ // Simulate loading a filter
+ list.update({
+ context: {
+ orderedBy: [{
+ name: 'date',
+ asc: false,
+ }, {
+ name: 'foo',
+ asc: true,
+ }]
+ }
+ });
+
+ list.destroy()
+ });
+
QUnit.test('many2one field rendering', function (assert) {
assert.expect(1);