summaryrefslogtreecommitdiff
path: root/addons/web
diff options
context:
space:
mode:
Diffstat (limited to 'addons/web')
-rw-r--r--addons/web/controllers/main.py19
-rw-r--r--addons/web/static/src/js/chrome/web_client.js7
-rw-r--r--addons/web/static/src/js/fields/basic_fields.js3
-rw-r--r--addons/web/static/src/js/fields/relational_fields.js15
-rw-r--r--addons/web/static/src/js/views/basic/basic_model.js13
-rw-r--r--addons/web/static/src/js/views/pivot/pivot_model.js3
-rw-r--r--addons/web/static/src/less/fields.less4
-rw-r--r--addons/web/static/tests/fields/basic_fields_tests.js29
-rw-r--r--addons/web/static/tests/fields/relational_fields_tests.js58
-rw-r--r--addons/web/static/tests/views/pivot_tests.js41
10 files changed, 178 insertions, 14 deletions
diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py
index ccf7b932669..c1eb41ac13e 100644
--- a/addons/web/controllers/main.py
+++ b/addons/web/controllers/main.py
@@ -961,14 +961,17 @@ class DataSet(http.Controller):
class View(http.Controller):
- @http.route('/web/view/add_custom', type='json', auth="user")
- def add_custom(self, view_id, arch):
- CustomView = request.env['ir.ui.view.custom']
- CustomView.create({
- 'user_id': request.session.uid,
- 'ref_id': view_id,
- 'arch': arch
- })
+ @http.route('/web/view/edit_custom', type='json', auth="user")
+ def edit_custom(self, custom_id, arch):
+ """
+ Edit a custom view
+
+ :param int custom_id: the id of the edited custom view
+ :param str arch: the edited arch of the custom view
+ :returns: dict with acknowledged operation (result set to True)
+ """
+ custom_view = request.env['ir.ui.view.custom'].browse(custom_id)
+ custom_view.write({ 'arch': arch })
return {'result': True}
class Binary(http.Controller):
diff --git a/addons/web/static/src/js/chrome/web_client.js b/addons/web/static/src/js/chrome/web_client.js
index 225599e0f7e..e2b3a23d2a6 100644
--- a/addons/web/static/src/js/chrome/web_client.js
+++ b/addons/web/static/src/js/chrome/web_client.js
@@ -91,6 +91,10 @@ return AbstractWebClient.extend({
bind_hashchange: function() {
var self = this;
$(window).bind('hashchange', this.on_hashchange);
+ var didHashChanged = false;
+ $(window).one('hashchange', function () {
+ didHashChanged = true;
+ });
var state = $.bbq.getState(true);
if (_.isEmpty(state) || state.action === "login") {
@@ -101,6 +105,9 @@ return AbstractWebClient.extend({
args: [[session.uid], ['action_id']],
})
.done(function(result) {
+ if (didHashChanged) {
+ return;
+ }
var data = result[0];
if(data.action_id) {
self.action_manager.doAction(data.action_id[0]);
diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js
index 0f426dedd3b..b14d71c0b26 100644
--- a/addons/web/static/src/js/fields/basic_fields.js
+++ b/addons/web/static/src/js/fields/basic_fields.js
@@ -1197,9 +1197,6 @@ var FieldBinaryImage = AbstractFieldBinary.extend({
self.do_warn(_t("Image"), _t("Could not display the selected image."));
});
},
- isSet: function () {
- return true;
- },
});
var FieldBinaryFile = AbstractFieldBinary.extend({
diff --git a/addons/web/static/src/js/fields/relational_fields.js b/addons/web/static/src/js/fields/relational_fields.js
index c3df03fcff9..423dbf40235 100644
--- a/addons/web/static/src/js/fields/relational_fields.js
+++ b/addons/web/static/src/js/fields/relational_fields.js
@@ -2335,6 +2335,21 @@ var FieldReference = FieldMany2One.extend({
},
//--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ /**
+ * @override
+ * @returns {jQuery}
+ */
+ getFocusableElement: function () {
+ if (this.mode === 'edit' && !this.field.relation) {
+ return this.$('select');
+ }
+ return this._super.apply(this, arguments);
+ },
+
+ //--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
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 962bb2899e1..e86d7dba8f7 100644
--- a/addons/web/static/src/js/views/basic/basic_model.js
+++ b/addons/web/static/src/js/views/basic/basic_model.js
@@ -3646,8 +3646,9 @@ var BasicModel = AbstractModel.extend({
if (isFieldInView) {
var field = r.fields[fieldName];
var fieldType = field.type;
+ var rec;
if (fieldType === 'many2one') {
- var rec = self._makeDataPoint({
+ rec = self._makeDataPoint({
context: r.context,
modelName: field.relation,
data: {id: r._changes[fieldName]},
@@ -3655,6 +3656,16 @@ var BasicModel = AbstractModel.extend({
});
r._changes[fieldName] = rec.id;
many2ones[fieldName] = true;
+ } else if (fieldType === 'reference') {
+ var reference = r._changes[fieldName].split(',');
+ rec = self._makeDataPoint({
+ context: r.context,
+ modelName: reference[0],
+ data: {id: parseInt(reference[1])},
+ parentID: r.id,
+ });
+ r._changes[fieldName] = rec.id;
+ many2ones[fieldName] = true;
} else if (_.contains(['one2many', 'many2many'], fieldType)) {
var x2mCommands = commands[0][2][fieldName];
defs.push(self._processX2ManyCommands(r, fieldName, x2mCommands));
diff --git a/addons/web/static/src/js/views/pivot/pivot_model.js b/addons/web/static/src/js/views/pivot/pivot_model.js
index 93201843633..4a5d9b45b4a 100644
--- a/addons/web/static/src/js/views/pivot/pivot_model.js
+++ b/addons/web/static/src/js/views/pivot/pivot_model.js
@@ -267,6 +267,7 @@ var PivotModel = AbstractModel.extend({
this.data.colGroupBys = params.context.pivot_column_groupby || this.data.colGroupBys;
this.data.groupedBy = params.context.pivot_row_groupby || this.data.groupedBy;
this.data.measures = this._processMeasures(params.context.pivot_measures) || this.data.measures;
+ this.defaultGroupedBy = this.data.groupedBy.length ? this.data.groupedBy : this.defaultGroupedBy;
}
if ('domain' in params) {
this.data.domain = params.domain;
@@ -295,7 +296,7 @@ var PivotModel = AbstractModel.extend({
self._updateTree(old_col_root, self.data.main_col.root);
new_groupby_length = self._getHeaderDepth(self.data.main_col.root) - 1;
- self.data.main_row.groupbys = old_col_root.groupbys.slice(0, new_groupby_length);
+ self.data.main_row.groupbys = old_row_root.groupbys.slice(0, new_groupby_length);
});
},
/**
diff --git a/addons/web/static/src/less/fields.less b/addons/web/static/src/less/fields.less
index ff6fe9eb20b..761e8577a29 100644
--- a/addons/web/static/src/less/fields.less
+++ b/addons/web/static/src/less/fields.less
@@ -361,6 +361,10 @@
&:hover .o_form_image_controls {
opacity: 0.8;
}
+
+ &.o_field_invalid > img {
+ border: 1px solid @brand-danger;
+ }
}
// Input loading/file
diff --git a/addons/web/static/tests/fields/basic_fields_tests.js b/addons/web/static/tests/fields/basic_fields_tests.js
index 6aa28fd9a2e..f2a49cbc387 100644
--- a/addons/web/static/tests/fields/basic_fields_tests.js
+++ b/addons/web/static/tests/fields/basic_fields_tests.js
@@ -1796,7 +1796,6 @@ QUnit.module('basic_fields', {
assert.strictEqual(form.$('div[name="document"] > img').css('max-width'), "90px",
"the image should correctly set its attributes");
form.destroy();
-
});
QUnit.test('image fields in subviews are loaded correctly', function (assert) {
@@ -1853,6 +1852,34 @@ QUnit.module('basic_fields', {
form.destroy();
});
+ QUnit.test('image fields with required attribute', function (assert) {
+ assert.expect(2);
+
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch: '<form string="Partners">' +
+ '<field name="document" required="1" widget="image"/>' +
+ '</form>',
+ mockRPC: function (route, args) {
+ if (args.method === 'create') {
+ throw new Error("Should not do a create RPC with unset required image field");
+ }
+ return this._super.apply(this, arguments);
+ },
+ });
+
+ form.$buttons.find('.o_form_button_save').click();
+
+ assert.ok(form.$('.o_form_view').hasClass('o_form_editable'),
+ "form view should still be editable");
+ assert.ok(form.$('.o_field_widget').hasClass('o_field_invalid'),
+ "image field should be displayed as invalid");
+
+ form.destroy();
+ });
+
QUnit.module('JournalDashboardGraph', {
beforeEach: function () {
_.extend(this.data.partner.fields, {
diff --git a/addons/web/static/tests/fields/relational_fields_tests.js b/addons/web/static/tests/fields/relational_fields_tests.js
index a046e79de6c..01739c7888c 100644
--- a/addons/web/static/tests/fields/relational_fields_tests.js
+++ b/addons/web/static/tests/fields/relational_fields_tests.js
@@ -11392,6 +11392,38 @@ QUnit.module('relational_fields', {
form.destroy();
});
+ QUnit.test('default_get a reference field in a x2m', function (assert) {
+ assert.expect(1);
+
+ this.data.partner.fields.turtles.default = [
+ [0, false, {turtle_ref: 'product,37'}]
+ ];
+
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch: '<form string="Partners">' +
+ '<sheet>' +
+ '<field name="turtles">' +
+ '<tree>' +
+ '<field name="turtle_ref"/>' +
+ '</tree>' +
+ '</field>' +
+ '</sheet>' +
+ '</form>',
+ viewOptions: {
+ mode: 'edit',
+ },
+ archs: {
+ 'turtle,false,form': '<form><field name="display_name"/><field name="turtle_ref"/></form>',
+ },
+ });
+ assert.strictEqual(form.$('.o_field_one2many[name="turtles"] .o_data_row:first').text(), "xphone",
+ "the default value should be correctly handled");
+ form.destroy();
+ });
+
QUnit.test('widget reference on char field, reset by onchange', function (assert) {
assert.expect(4);
@@ -11442,6 +11474,32 @@ QUnit.module('relational_fields', {
form.destroy();
});
+ QUnit.test('reference and list navigation', function (assert) {
+ assert.expect(2);
+
+ var list = createView({
+ View: ListView,
+ model: 'partner',
+ data: this.data,
+ arch: '<tree editable="bottom"><field name="reference"/></tree>',
+ });
+
+ // edit first row
+ list.$('.o_data_row .o_data_cell').first().click();
+ assert.strictEqual(list.$('.o_data_row:eq(0) .o_field_widget[name="reference"] input')[0], document.activeElement,
+ 'input of first data row should be selected');
+
+ // press TAB to go to next line
+ list.$('.o_data_row:eq(0) input').trigger($.Event('keydown', {
+ which: $.ui.keyCode.TAB,
+ keyCode: $.ui.keyCode.TAB,
+ }));
+ assert.strictEqual(list.$('.o_data_row:eq(1) .o_field_widget[name="reference"] select')[0], document.activeElement,
+ 'select of second data row should be selected');
+
+ list.destroy();
+ });
+
QUnit.test('one2many with extra field from server not in form', function (assert) {
assert.expect(6);
diff --git a/addons/web/static/tests/views/pivot_tests.js b/addons/web/static/tests/views/pivot_tests.js
index 77c1c66fae8..26733a5edca 100644
--- a/addons/web/static/tests/views/pivot_tests.js
+++ b/addons/web/static/tests/views/pivot_tests.js
@@ -1083,4 +1083,45 @@ QUnit.module('Views', {
'should have loaded the proper data');
pivot.destroy();
});
+
+ QUnit.test('Row and column groupbys plus a domain', function (assert) {
+ assert.expect(3);
+
+ var pivot = createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot>' +
+ '<field name="foo" type="measure"/>' +
+ '</pivot>',
+ });
+
+ // Set a column groupby
+ pivot.$('thead .o_pivot_header_cell_closed').click();
+ pivot.$('.o_field_selection li[data-field=customer] a').click();
+
+ // Set a Row groupby
+ pivot.$('tbody .o_pivot_header_cell_closed').click();
+ pivot.$('.o_pivot_field_menu li[data-field=product_id] a').click();
+
+ // Set a domain
+ pivot.update({domain: [['product_id', '=', 41]]});
+
+ var expectedContext = {pivot_column_groupby: ['customer'],
+ pivot_measures: ['foo'],
+ pivot_row_groupby: ['product_id']};
+
+ // Mock 'save as favorite'
+ assert.deepEqual(pivot.getContext(), expectedContext,
+ 'The pivot view should have the right context');
+
+ var $xpadHeader = pivot.$('tbody .o_pivot_header_cell_closed[data-original-title=Product]');
+ assert.equal($xpadHeader.length, 1,
+ 'There should be only one product line because of the domain');
+
+ assert.equal($xpadHeader.text(), 'xpad',
+ 'The product should be the right one');
+
+ pivot.destroy();
+ });
});});