- var message = d.message ? d.message : d.error.data.message;
+ var message = (d.message !== undefined) ? d.message : d.error.data.message;
d.html_error = context.engine.tools.html_escape(message).replace(/\n/g, '
');
@@ -836,11 +836,6 @@
t-att-width="widget.node.attrs.img_width || widget.node.attrs.width"
t-att-height="widget.node.attrs.img_height || widget.node.attrs.height"/>
-
-
-
![]()
-
-
diff --git a/addons/web/static/tests/fields/relational_fields_tests.js b/addons/web/static/tests/fields/relational_fields_tests.js
index 4f326e14f98..127d5761fd2 100644
--- a/addons/web/static/tests/fields/relational_fields_tests.js
+++ b/addons/web/static/tests/fields/relational_fields_tests.js
@@ -482,6 +482,183 @@ QUnit.module('relational_fields', {
// assert.strictEqual(form.$('input').eq(1).val(), 'xphone', "onchange should have been applied");
// });
+ QUnit.test('form: quick create then save directly', function (assert) {
+ var done = assert.async();
+ assert.expect(5);
+
+ var M2O_DELAY = relationalFields.FieldMany2One.prototype.AUTOCOMPLETE_DELAY;
+ relationalFields.FieldMany2One.prototype.AUTOCOMPLETE_DELAY = 0;
+
+ var def = $.Deferred();
+ var newRecordID;
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch: '',
+ mockRPC: function (route, args) {
+ var result = this._super.apply(this, arguments);
+ if (args.method === 'name_create') {
+ assert.step('name_create');
+ return def.then(_.constant(result)).then(function (nameGet) {
+ newRecordID = nameGet[0];
+ return nameGet;
+ });
+ }
+ if (args.method === 'create') {
+ assert.step('create');
+ assert.strictEqual(args.args[0].trululu, newRecordID,
+ "should create with the correct m2o id");
+ }
+ return result;
+ },
+ });
+
+ var $dropdown = form.$('.o_field_many2one input').autocomplete('widget');
+ form.$('.o_field_many2one input').val('b').trigger('keydown');
+ concurrency.delay(0).then(function () {
+ $dropdown.find('li:first()').click(); // quick create 'b'
+ form.$buttons.find('.o_form_button_save').click();
+
+ assert.verifySteps(['name_create'],
+ "should wait for the name_create before creating the record");
+
+ def.resolve();
+
+ assert.verifySteps(['name_create', 'create']);
+
+ relationalFields.FieldMany2One.prototype.AUTOCOMPLETE_DELAY = M2O_DELAY;
+ form.destroy();
+ done();
+ });
+ });
+
+ QUnit.test('list: quick create then save directly', function (assert) {
+ var done = assert.async();
+ assert.expect(8);
+
+ var M2O_DELAY = relationalFields.FieldMany2One.prototype.AUTOCOMPLETE_DELAY;
+ relationalFields.FieldMany2One.prototype.AUTOCOMPLETE_DELAY = 0;
+
+ var def = $.Deferred();
+ var newRecordID;
+ var list = createView({
+ View: ListView,
+ model: 'partner',
+ data: this.data,
+ arch: '' +
+ '' +
+ '',
+ mockRPC: function (route, args) {
+ var result = this._super.apply(this, arguments);
+ if (args.method === 'name_create') {
+ assert.step('name_create');
+ return def.then(_.constant(result)).then(function (nameGet) {
+ newRecordID = nameGet[0];
+ return nameGet;
+ });
+ }
+ if (args.method === 'create') {
+ assert.step('create');
+ assert.strictEqual(args.args[0].trululu, newRecordID,
+ "should create with the correct m2o id");
+ }
+ return result;
+ },
+ });
+
+ list.$buttons.find('.o_list_button_add').click();
+
+ var $dropdown = list.$('.o_field_many2one input').autocomplete('widget');
+ list.$('.o_field_many2one input').val('b').trigger('keydown');
+ concurrency.delay(0).then(function () {
+ $dropdown.find('li:first()').click(); // quick create 'b'
+ list.$buttons.find('.o_list_button_add').click();
+
+ assert.verifySteps(['name_create'],
+ "should wait for the name_create before creating the record");
+ assert.strictEqual(list.$('.o_data_row').length, 4,
+ "should wait for the name_create before adding the new row");
+
+ def.resolve();
+
+ assert.verifySteps(['name_create', 'create']);
+ assert.strictEqual(list.$('.o_data_row:nth(1) .o_data_cell').text(), 'b',
+ "created row should have the correct m2o value");
+ assert.strictEqual(list.$('.o_data_row').length, 5,
+ "should have added the fifth row");
+
+ relationalFields.FieldMany2One.prototype.AUTOCOMPLETE_DELAY = M2O_DELAY;
+ list.destroy();
+ done();
+ });
+ });
+
+ QUnit.test('list in form: quick create then save directly', function (assert) {
+ var done = assert.async();
+ assert.expect(6);
+
+ var M2O_DELAY = relationalFields.FieldMany2One.prototype.AUTOCOMPLETE_DELAY;
+ relationalFields.FieldMany2One.prototype.AUTOCOMPLETE_DELAY = 0;
+
+ var def = $.Deferred();
+ var newRecordID;
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch: '',
+ mockRPC: function (route, args) {
+ var result = this._super.apply(this, arguments);
+ if (args.method === 'name_create') {
+ assert.step('name_create');
+ return def.then(_.constant(result)).then(function (nameGet) {
+ newRecordID = nameGet[0];
+ return nameGet;
+ });
+ }
+ if (args.method === 'create') {
+ assert.step('create');
+ assert.strictEqual(args.args[0].p[0][2].trululu, newRecordID,
+ "should create with the correct m2o id");
+ }
+ return result;
+ },
+ });
+
+ form.$('.o_field_x2many_list_row_add a').click();
+
+ var $dropdown = form.$('.o_field_many2one input').autocomplete('widget');
+ form.$('.o_field_many2one input').val('b').trigger('keydown');
+ concurrency.delay(0).then(function () {
+ $dropdown.find('li:first()').click(); // quick create 'b'
+ form.$buttons.find('.o_form_button_save').click();
+
+ assert.verifySteps(['name_create'],
+ "should wait for the name_create before creating the record");
+
+ def.resolve();
+
+ assert.verifySteps(['name_create', 'create']);
+ assert.strictEqual(form.$('.o_data_row:first .o_data_cell').text(), 'b',
+ "first row should have the correct m2o value");
+
+ relationalFields.FieldMany2One.prototype.AUTOCOMPLETE_DELAY = M2O_DELAY;
+ form.destroy();
+ done();
+ });
+ });
+
QUnit.test('autocompletion in a many2one, in form view with a domain', function (assert) {
assert.expect(1);
@@ -3852,6 +4029,115 @@ QUnit.module('relational_fields', {
form.destroy();
});
+ QUnit.test('editable list: value reset by an onchange', function (assert) {
+ // this test reproduces a subtle behavior that may occur in a form view:
+ // the user adds a record in a one2many field, and directly clicks on a
+ // datetime field of the form view which has an onchange, which totally
+ // overrides the value of the one2many (commands 5 and 0). The handler
+ // that switches the edited row to readonly is then called after the
+ // new value of the one2many field is applied (the one returned by the
+ // onchange), so the row that must go to readonly doesn't exist anymore.
+ assert.expect(2);
+
+ this.data.partner.onchanges = {
+ datetime: function (obj) {
+ obj.turtles = [[5], [0, 0, {display_name: 'new'}]];
+ },
+ };
+
+ var def;
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch: '',
+ mockRPC: function (route, args) {
+ var result = this._super.apply(this, arguments);
+ if (args.method === 'onchange') {
+ return $.when(def).then(_.constant(result));
+ }
+ return result;
+ },
+ });
+
+ // trigger the two onchanges
+ form.$('.o_field_x2many_list_row_add a').click();
+ form.$('.o_data_row .o_field_widget').val('a name').trigger('input');
+ def = $.Deferred();
+ form.$('.o_datepicker_input').click(); // focusout o2m and set value to today
+
+ // resolve the onchange def
+ def.resolve();
+
+ assert.strictEqual(form.$('.o_data_row').length, 1,
+ "should have one record in the o2m");
+ assert.strictEqual(form.$('.o_data_row .o_data_cell').text(), 'new',
+ "should be the record created by the onchange");
+
+ form.destroy();
+ });
+
+ QUnit.test('editable list: onchange that returns a warning', function (assert) {
+ assert.expect(5);
+
+ this.data.turtle.onchanges = {
+ display_name: function () {},
+ };
+
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch: '',
+ res_id: 1,
+ mockRPC: function (route, args) {
+ if (args.method === 'onchange') {
+ assert.step(args.method);
+ return $.when({
+ value: {},
+ warning: {
+ title: "Warning",
+ message: "You must first select a partner"
+ },
+ });
+ }
+ return this._super.apply(this, arguments);
+ },
+ viewOptions: {
+ mode: 'edit',
+ },
+ intercepts: {
+ warning: function () {
+ assert.step('warning');
+ },
+ },
+ });
+
+ // add a line (this should trigger an onchange and a warning)
+ form.$('.o_field_x2many_list_row_add a').click();
+
+ // check if 'Add an item' still works (this should trigger an onchange
+ // and a warning again)
+ form.$('.o_field_x2many_list_row_add a').click();
+
+ assert.verifySteps(['onchange', 'warning', 'onchange', 'warning']);
+
+ form.destroy();
+ });
+
QUnit.module('FieldMany2Many');
QUnit.test('many2many kanban: edition', function (assert) {
diff --git a/addons/web/static/tests/helpers/test_utils.js b/addons/web/static/tests/helpers/test_utils.js
index eae31e68e80..bc2cf05f360 100644
--- a/addons/web/static/tests/helpers/test_utils.js
+++ b/addons/web/static/tests/helpers/test_utils.js
@@ -13,6 +13,7 @@ odoo.define('web.test_utils', function (require) {
var basic_fields = require('web.basic_fields');
var config = require('web.config');
var core = require('web.core');
+var dom = require('web.dom');
var session = require('web.session');
var MockServer = require('web.MockServer');
var Widget = require('web.Widget');
@@ -172,7 +173,14 @@ function createAsyncView(params) {
widget.destroy();
$('#qunit-fixture').off('DOMNodeInserted.removeSRC');
};
- return view.appendTo($view_manager).then(function () {
+ // render the view in a fragment as they must be able to render correctly
+ // without being in the DOM
+ var fragment = document.createDocumentFragment();
+ return view.appendTo(fragment).then(function () {
+ dom.append($view_manager, fragment, {
+ callbacks: [{widget: view}],
+ in_DOM: true,
+ });
view.$el.on('click', 'a', function (ev) {
ev.preventDefault();
});
@@ -244,52 +252,53 @@ function addMockEnvironment(widget, params) {
logLevel: params.logLevel,
currentDate: params.currentDate,
});
- var widgetDestroy;
-
// make sure the debounce value for input fields is set to 0
var initialDebounce = DebouncedField.prototype.DEBOUNCE;
DebouncedField.prototype.DEBOUNCE = params.fieldDebounce || 0;
-
+ var initialSession, initialConfig, initialParameters;
if ('session' in params) {
- var initialSession = _.extend({}, session);
+ initialSession = _.extend({}, session);
_.extend(session, params.session);
- widgetDestroy = widget.destroy;
- widget.destroy = function () {
- for (var key in session) {
+ }
+ if ('config' in params) {
+ initialConfig = _.extend({}, config);
+ _.extend(config, params.config);
+ }
+ if ('translateParameters' in params) {
+ initialParameters = _.extend({}, core._t.database.parameters);
+ _.extend(core._t.database.parameters, params.translateParameters);
+ }
+
+ var widgetDestroy = widget.destroy;
+ widget.destroy = function () {
+ // clear the caches (e.g. data_manager, ModelFieldSelector) when the
+ // widget is destroyed, at the end of each test to avoid collisions
+ core.bus.trigger('clear_cache');
+
+ DebouncedField.prototype.DEBOUNCE = initialDebounce;
+
+ var key;
+ if ('session' in params) {
+ for (key in session) {
delete session[key];
}
_.extend(session, initialSession);
- widgetDestroy.call(this);
- };
- }
-
- if ('config' in params) {
- var initialConfig = _.extend({}, config);
- _.extend(config, params.config);
- widgetDestroy = widget.destroy;
- widget.destroy = function () {
- for (var key in config) {
+ }
+ if ('config' in params) {
+ for (key in config) {
delete config[key];
}
_.extend(config, initialConfig);
- DebouncedField.prototype.DEBOUNCE = initialDebounce;
- widgetDestroy.call(this);
- };
- }
-
- if ('translateParameters' in params) {
- var initialParameters = _.extend({}, core._t.database.parameters);
- _.extend(core._t.database.parameters, params.translateParameters);
- var oldDestroy = widget.destroy;
- widget.destroy = function () {
- for (var key in core._t.database.parameters) {
+ }
+ if ('translateParameters' in params) {
+ for (key in core._t.database.parameters) {
delete core._t.database.parameters[key];
}
_.extend(core._t.database.parameters, initialParameters);
- oldDestroy.call(this);
- };
- }
+ }
+ widgetDestroy.call(this);
+ };
intercept(widget, 'call_service', function (event) {
if (event.data.service === 'ajax') {
diff --git a/addons/web/static/tests/views/basic_model_tests.js b/addons/web/static/tests/views/basic_model_tests.js
index 582e06ba62b..fefc7231e5b 100644
--- a/addons/web/static/tests/views/basic_model_tests.js
+++ b/addons/web/static/tests/views/basic_model_tests.js
@@ -1165,6 +1165,54 @@ QUnit.module('Views', {
model.destroy();
});
+ QUnit.test('default_get: fetch x2manys inside x2manys', function (assert) {
+ assert.expect(3);
+
+ this.data.partner.fields.o2m = {
+ string: "O2M", type: 'one2many', relation: 'partner', default: [[6, 0, [1]]],
+ };
+
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ });
+
+ var params = {
+ fieldNames: ['o2m'],
+ fields: this.data.partner.fields,
+ fieldsInfo: {
+ form: {
+ o2m: {
+ relatedFields: this.data.partner.fields,
+ fieldsInfo: {
+ list: {
+ category: {
+ relatedFields: { display_name: {} },
+ },
+ },
+ },
+ viewType: 'list',
+ },
+ },
+ },
+ modelName: 'partner',
+ type: 'record',
+ viewType: 'form',
+ };
+
+ model.load(params).then(function (resultID) {
+ var record = model.get(resultID);
+ assert.strictEqual(record.data.o2m.count, 1, "o2m field should contain 1 record");
+ var categoryList = record.data.o2m.data[0].data.category;
+ assert.strictEqual(categoryList.count, 1,
+ "category field should contain 1 record");
+ assert.strictEqual(categoryList.data[0].data.display_name,
+ 'gold', "category records should have been fetched");
+ });
+
+ model.destroy();
+ });
+
QUnit.test('contexts and domains can be properly fetched', function (assert) {
assert.expect(8);
@@ -1448,6 +1496,7 @@ QUnit.module('Views', {
fieldsInfo: {
form: {
product_ids: {
+ relatedFields: this.data.product.fields,
fieldsInfo: { list: { name: {}, date: {} } },
viewType: 'list',
}
diff --git a/addons/web/static/tests/views/form_tests.js b/addons/web/static/tests/views/form_tests.js
index a07f022eb74..3f829a654b7 100644
--- a/addons/web/static/tests/views/form_tests.js
+++ b/addons/web/static/tests/views/form_tests.js
@@ -837,6 +837,28 @@ QUnit.module('Views', {
form.destroy();
});
+ QUnit.test('invisible attrs on separators', function (assert) {
+ assert.expect(1);
+
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch: '',
+ res_id: 1,
+ });
+ assert.strictEqual(form.$('div.o_horizontal_separator').hasClass('o_invisible_modifier'), true,
+ "separator div should be hidden");
+ form.destroy();
+ });
+
QUnit.test('buttons in form view', function (assert) {
assert.expect(7);
@@ -3770,7 +3792,7 @@ QUnit.module('Views', {
});
form.$buttons.find('.o_form_button_edit').click();
- assert.strictEqual(form.$('input[name="foo"]')[0], document.activeElement,
+ assert.strictEqual(document.activeElement, form.$('input[name="foo"]')[0],
"foo field should have focus");
assert.strictEqual(form.$('input[name="foo"]')[0].selectionStart, 3,
"cursor should be at the end");
@@ -3792,7 +3814,25 @@ QUnit.module('Views', {
res_id: 1,
});
form.$buttons.find('.o_form_button_edit').click();
- assert.strictEqual(form.$('input[name="foo"]')[0], document.activeElement,
+ assert.strictEqual(document.activeElement, form.$('input[name="foo"]')[0],
+ "foo field should have focus");
+
+ form.destroy();
+ });
+
+ QUnit.test('in create mode, autofocus fields are focused', function (assert) {
+ assert.expect(1);
+
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch: '',
+ });
+ assert.strictEqual(document.activeElement, form.$('input[name="foo"]')[0],
"foo field should have focus");
form.destroy();
@@ -3821,6 +3861,24 @@ QUnit.module('Views', {
form.destroy();
});
+ QUnit.test('autofocus first visible field', function (assert) {
+ assert.expect(1);
+
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch: '',
+ });
+ assert.strictEqual(document.activeElement, form.$('input[name="foo"]')[0],
+ "foo field should have focus");
+
+ form.destroy();
+ });
+
QUnit.test('open one2many form containing many2many_tags', function (assert) {
assert.expect(4);
@@ -4893,5 +4951,40 @@ QUnit.module('Views', {
form.destroy();
});
+ QUnit.test('wow effect', function (assert) {
+ assert.expect(3);
+
+ this.data.partner.fields.model_name = { string: "Model name", type: "char" };
+
+ var clickCount = 0;
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch: '',
+ intercepts: {
+ execute_action: function (event) {
+ if (clickCount === 0) {
+ event.data.on_success();
+ } else {
+ event.data.on_fail();
+ }
+ clickCount++;
+ },
+ show_wow: function () {
+ assert.step('wow');
+ },
+ },
+ });
+
+ form.$('button.o_wow').click();
+ assert.verifySteps(['wow']);
+ form.$('button.o_wow').click();
+ assert.verifySteps(['wow']);
+
+ form.destroy();
+ });
});
});
diff --git a/addons/web/static/tests/views/list_tests.js b/addons/web/static/tests/views/list_tests.js
index e1cfef2b23b..332ba6687f9 100644
--- a/addons/web/static/tests/views/list_tests.js
+++ b/addons/web/static/tests/views/list_tests.js
@@ -84,7 +84,7 @@ QUnit.module('Views', {
QUnit.module('ListView');
QUnit.test('simple readonly list', function (assert) {
- assert.expect(9);
+ assert.expect(10);
var list = createView({
View: ListView,
@@ -93,6 +93,9 @@ QUnit.module('Views', {
arch: '',
});
+ assert.notOk(list.$el.hasClass('o_cannot_create'),
+ "should not have className 'o_cannot_create'");
+
// 3 th (1 for checkbox, 2 for columns)
assert.strictEqual(list.$('th').length, 3, "should have 3 columns");
@@ -114,6 +117,24 @@ QUnit.module('Views', {
list.destroy();
});
+ QUnit.test('list with create="0"', function (assert) {
+ assert.expect(2);
+
+ var list = createView({
+ View: ListView,
+ model: 'foo',
+ data: this.data,
+ arch: '',
+ });
+
+ assert.ok(list.$el.hasClass('o_cannot_create'),
+ "should have className 'o_cannot_create'");
+ assert.strictEqual(list.$buttons.find('.o_list_button_add').length, 0,
+ "should not have the 'Create' button");
+
+ list.destroy();
+ });
+
QUnit.test('simple editable rendering', function (assert) {
assert.expect(9);
diff --git a/addons/website/models/website.py b/addons/website/models/website.py
index fe79a8ac1a3..0b407e4d753 100644
--- a/addons/website/models/website.py
+++ b/addons/website/models/website.py
@@ -397,9 +397,10 @@ class Website(models.Model):
@api.model
def get_current_website(self):
- domain_name = request.httprequest.environ.get('HTTP_HOST', '').split(':')[0]
+ domain_name = request and request.httprequest.environ.get('HTTP_HOST', '').split(':')[0] or None
website_id = self._get_current_website_id(domain_name)
- request.context = dict(request.context, website_id=website_id)
+ if request:
+ request.context = dict(request.context, website_id=website_id)
return self.browse(website_id)
@tools.cache('domain_name')
diff --git a/addons/website/static/src/js/website.ace.js b/addons/website/static/src/js/website.ace.js
index 6e0e4437487..2ce486fc1c8 100644
--- a/addons/website/static/src/js/website.ace.js
+++ b/addons/website/static/src/js/website.ace.js
@@ -3,18 +3,23 @@ odoo.define('website.ace', function (require) {
var Widget = require('web.Widget');
var website = require('website.website');
+var mixins = require('web.mixins');
var ViewEditor = require('web_editor.ace');
var hash = "#advanced-view-editor";
-var Ace = Widget.extend({
+var Ace = Widget.extend(mixins.ServiceProvider, {
events: {
"click a[data-action=ace]": function (e) {
e.preventDefault();
this.launchAce();
},
},
+ init: function () {
+ mixins.ServiceProvider.init.apply(this, arguments);
+ this._super.apply(this, arguments);
+ },
start: function () {
if (window.location.hash.substr(0, hash.length) === hash) {
this.launchAce();
diff --git a/addons/website/static/src/js/website.seo.js b/addons/website/static/src/js/website.seo.js
index 219728aa552..a05fc4a5883 100644
--- a/addons/website/static/src/js/website.seo.js
+++ b/addons/website/static/src/js/website.seo.js
@@ -6,6 +6,7 @@ var ajax = require('web.ajax');
var Class = require('web.Class');
var Dialog = require('web.Dialog');
var mixins = require('web.mixins');
+var rpc = require("web.rpc");
var Widget = require('web.Widget');
var base = require('web_editor.base');
var website = require('website.website');
@@ -490,22 +491,21 @@ var Configurator = Dialog.extend({
def.resolve(null);
} else {
var fields = ['website_meta_title', 'website_meta_description', 'website_meta_keywords'];
- this._rpc({
- model: obj.model,
- method: 'read',
- args: [[obj.id], fields, base.get_context()],
- })
- .then(function (data) {
- if (data.length) {
- var meta = data[0];
- meta.model = obj.model;
- def.resolve(meta);
- } else {
- def.resolve(null);
- }
- }).fail(function () {
- def.reject();
- });
+ rpc.query({
+ model: obj.model,
+ method: 'read',
+ args: [[obj.id], fields, base.get_context()],
+ }).then(function (data) {
+ if (data.length) {
+ var meta = data[0];
+ meta.model = obj.model;
+ def.resolve(meta);
+ } else {
+ def.resolve(null);
+ }
+ }).fail(function () {
+ def.reject();
+ });
}
return def;
},
@@ -514,11 +514,11 @@ var Configurator = Dialog.extend({
if (!obj) {
return $.Deferred().reject();
} else {
- return this._rpc({
- model: obj.model,
- method: 'write',
- args: [[obj.id], data, base.get_context()],
- });
+ return rpc.query({
+ model: obj.model,
+ method: 'write',
+ args: [[obj.id], data, base.get_context()],
+ });
}
},
titleChanged: function () {
diff --git a/addons/website_crm_partner_assign/models/crm_lead.py b/addons/website_crm_partner_assign/models/crm_lead.py
index f501a64eefa..f1f002d5dd0 100644
--- a/addons/website_crm_partner_assign/models/crm_lead.py
+++ b/addons/website_crm_partner_assign/models/crm_lead.py
@@ -48,8 +48,6 @@ class CrmLead(models.Model):
for salesman_id, leads_ids in pycompat.items(salesmans_leads):
leads = self.browse(leads_ids)
leads.write({'user_id': salesman_id})
- for lead in leads:
- lead._onchange_user_id()
@api.multi
def action_assign_partner(self):
diff --git a/addons/website_event_sale/static/src/js/website.tour.event_sale.js b/addons/website_event_sale/static/src/js/website.tour.event_sale.js
index 8a29796dab4..895e6191f86 100644
--- a/addons/website_event_sale/static/src/js/website.tour.event_sale.js
+++ b/addons/website_event_sale/static/src/js/website.tour.event_sale.js
@@ -75,7 +75,7 @@ tour.register('event_buy_tickets', {
trigger: 'a[href="/shop/confirm_order"]:contains("Confirm")',
},
{
- content: "Check that the subtotal is 5,500.00",
+ content: "Check that the subtotal is 5,500.00 USD", // this test will fail if the currency of the main company is not USD
trigger: '#order_total_untaxed .oe_currency_value:contains("5,500.00")',
run: function () {}, // it's a check
},
diff --git a/addons/website_forum/i18n/website_forum.pot b/addons/website_forum/i18n/website_forum.pot
index 5fddf1e74a5..2f70c77241b 100644
--- a/addons/website_forum/i18n/website_forum.pot
+++ b/addons/website_forum/i18n/website_forum.pot
@@ -604,6 +604,7 @@ msgid "Bad Request"
msgstr ""
#. module: website_forum
+#: model:ir.model.fields,field_description:website_forum.field_res_users_badge_ids
#: model:ir.ui.view,arch_db:website_forum.badge
#: model:ir.ui.view,arch_db:website_forum.header
#: model:ir.ui.view,arch_db:website_forum.user_detail_full
@@ -782,6 +783,7 @@ msgstr ""
#. openerp-web
#: code:addons/website_forum/static/src/xml/website_forum_share_templates.xml:46
#: selection:forum.post,state:0
+#: model:ir.ui.view,arch_db:website_forum.post_description_full
#, python-format
msgid "Close"
msgstr ""
@@ -883,6 +885,16 @@ msgstr ""
msgid "Convert all answers to comments and vice versa"
msgstr ""
+#. module: website_forum
+#: model:ir.ui.view,arch_db:website_forum.post_answers
+msgid "Convert as a comment"
+msgstr ""
+
+#. module: website_forum
+#: model:ir.ui.view,arch_db:website_forum.post_comment
+msgid "Convert as an answer"
+msgstr ""
+
#. module: website_forum
#: model:ir.model.fields,field_description:website_forum.field_forum_post_karma_accept
msgid "Convert comment to answer"
@@ -968,6 +980,12 @@ msgstr ""
msgid "Default Post"
msgstr ""
+#. module: website_forum
+#: model:ir.ui.view,arch_db:website_forum.post_answers
+#: model:ir.ui.view,arch_db:website_forum.post_description_full
+msgid "Delete"
+msgstr ""
+
#. module: website_forum
#: model:ir.model.fields,field_description:website_forum.field_forum_forum_karma_unlink_all
msgid "Delete all posts"
@@ -1038,6 +1056,8 @@ msgstr ""
#. module: website_forum
#: model:ir.ui.view,arch_db:website_forum.edit_post
+#: model:ir.ui.view,arch_db:website_forum.post_answers
+#: model:ir.ui.view,arch_db:website_forum.post_description_full
msgid "Edit"
msgstr ""
@@ -1184,6 +1204,11 @@ msgstr ""
msgid "First upvote"
msgstr ""
+#. module: website_forum
+#: model:ir.ui.view,arch_db:website_forum.post_description_full
+msgid "Flag"
+msgstr ""
+
#. module: website_forum
#: model:ir.model.fields,field_description:website_forum.field_forum_forum_karma_flag
#: model:ir.model.fields,field_description:website_forum.field_forum_post_karma_flag
@@ -1193,6 +1218,7 @@ msgstr ""
#. module: website_forum
#: selection:forum.post,state:0
#: model:ir.ui.view,arch_db:website_forum.moderation_queue
+#: model:ir.ui.view,arch_db:website_forum.post_description_full
msgid "Flagged"
msgstr ""
@@ -2015,6 +2041,11 @@ msgstr ""
msgid "Re: %s"
msgstr ""
+#. module: website_forum
+#: model:ir.ui.view,arch_db:website_forum.post_description_full
+msgid "Reactivate"
+msgstr ""
+
#. module: website_forum
#: model:ir.ui.view,arch_db:website_forum.header
msgid "Read Guidelines"
@@ -2067,6 +2098,11 @@ msgstr ""
msgid "Relevance Computation"
msgstr ""
+#. module: website_forum
+#: model:ir.ui.view,arch_db:website_forum.post_description_full
+msgid "Reopen"
+msgstr ""
+
#. module: website_forum
#: model:ir.ui.view,arch_db:website_forum.post_answers
#: model:ir.ui.view,arch_db:website_forum.post_description_full
diff --git a/addons/website_forum/static/src/xml/website_forum_share_templates.xml b/addons/website_forum/static/src/xml/website_forum_share_templates.xml
index ab44aa48fcb..670e143ced8 100644
--- a/addons/website_forum/static/src/xml/website_forum_share_templates.xml
+++ b/addons/website_forum/static/src/xml/website_forum_share_templates.xml
@@ -33,7 +33,7 @@
By sharing you answer, you will get additional karma points if your
answer is selected as the right one. See what you can do with karma
- here.
+ here.
Share this content to increase your chances to be featured on the front page and attract more visitors.
diff --git a/addons/website_forum/views/website_forum.xml b/addons/website_forum/views/website_forum.xml
index 74df06b10cb..3608b9b47c7 100644
--- a/addons/website_forum/views/website_forum.xml
+++ b/addons/website_forum/views/website_forum.xml
@@ -793,9 +793,10 @@