diff --git a/addons/point_of_sale/static/src/js/chrome.js b/addons/point_of_sale/static/src/js/chrome.js
index 3d95af17a61..92c1316fcb7 100644
--- a/addons/point_of_sale/static/src/js/chrome.js
+++ b/addons/point_of_sale/static/src/js/chrome.js
@@ -572,7 +572,7 @@ var Chrome = PosBaseWidget.extend({
this.started = new $.Deferred(); // resolves when DOM is online
this.ready = new $.Deferred(); // resolves when the whole GUI has been loaded
- this.pos = new models.PosModel(this.session,{chrome:this});
+ this.pos = new models.PosModel(this.getSession(), {chrome:this});
this.gui = new gui.Gui({pos: this.pos, chrome: this});
this.chrome = this; // So that chrome's childs have chrome set automatically
this.pos.gui = this.gui;
diff --git a/addons/sale_mrp/models/sale_mrp.py b/addons/sale_mrp/models/sale_mrp.py
index 20ddf2c3fc1..fe789333d03 100644
--- a/addons/sale_mrp/models/sale_mrp.py
+++ b/addons/sale_mrp/models/sale_mrp.py
@@ -36,14 +36,14 @@ class SaleOrderLine(models.Model):
if uom.id != components[product]['uom']:
from_uom = uom
to_uom = self.env['product.uom'].browse(components[product]['uom'])
- qty = from_uom._compute_quantity(qty, to_uom_id=to_uom)
+ qty = from_uom._compute_quantity(qty, to_uom)
components[product]['qty'] += qty
else:
# To be in the uom reference of the product
to_uom = self.env['product.product'].browse(product).uom_id
if uom.id != to_uom.id:
from_uom = uom
- qty = from_uom._compute_quantity(qty, to_uom_id=to_uom)
+ qty = from_uom._compute_quantity(qty, to_uom)
components[product] = {'qty': qty, 'uom': to_uom.id}
return components
diff --git a/addons/stock/models/stock_location.py b/addons/stock/models/stock_location.py
index 35329d1616a..db80965fbfe 100644
--- a/addons/stock/models/stock_location.py
+++ b/addons/stock/models/stock_location.py
@@ -3,6 +3,7 @@
from datetime import datetime
from dateutil import relativedelta
+from odoo.exceptions import UserError
from odoo import api, fields, models, _
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT
@@ -86,6 +87,8 @@ class Location(models.Model):
name = location.name
while location.location_id and location.usage != 'view':
location = location.location_id
+ if not name:
+ raise UserError(_('You have to set a name for this location.'))
name = location.name + "/" + name
ret_list.append((orig_location.id, name))
return ret_list
diff --git a/addons/web/static/src/js/chrome/abstract_web_client.js b/addons/web/static/src/js/chrome/abstract_web_client.js
index 8492adc33f6..9a629ec3393 100644
--- a/addons/web/static/src/js/chrome/abstract_web_client.js
+++ b/addons/web/static/src/js/chrome/abstract_web_client.js
@@ -245,7 +245,7 @@ var AbstractWebClient = Widget.extend(mixins.ServiceProvider, {
},
do_reload: function () {
var self = this;
- return this.session.session_reload().then(function () {
+ return session.session_reload().then(function () {
session.load_modules(true).then(
self.menu.proxy('do_reload'));
});
diff --git a/addons/web/static/src/js/chrome/action_manager.js b/addons/web/static/src/js/chrome/action_manager.js
index e18a3d60734..194b8e6c8a7 100644
--- a/addons/web/static/src/js/chrome/action_manager.js
+++ b/addons/web/static/src/js/chrome/action_manager.js
@@ -912,14 +912,14 @@ var ActionManager = Widget.extend({
action: JSON.stringify(action),
token: new Date().getTime()
};
- var url = self.session.url('/web/report', params);
+ var url = session.url('/web/report', params);
framework.unblockUI();
$('
')[0].click();
return;
}
var c = crash_manager;
return $.Deferred(function (d) {
- self.session.get_file({
+ session.get_file({
url: '/web/report',
data: {action: JSON.stringify(action)},
complete: framework.unblockUI,
diff --git a/addons/web/static/src/js/fields/abstract_field.js b/addons/web/static/src/js/fields/abstract_field.js
index 894ee57c813..e25c02f781e 100644
--- a/addons/web/static/src/js/fields/abstract_field.js
+++ b/addons/web/static/src/js/fields/abstract_field.js
@@ -457,7 +457,7 @@ var AbstractField = Widget.extend({
this.trigger_up('navigation_move', {direction: 'next_line'});
break;
case $.ui.keyCode.ESCAPE:
- this.trigger_up('navigation_move', {direction: 'cancel'});
+ this.trigger_up('navigation_move', {direction: 'cancel', originalEvent: ev});
break;
case $.ui.keyCode.UP:
ev.stopPropagation();
diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js
index 1139b234edd..6392870dc90 100644
--- a/addons/web/static/src/js/fields/basic_fields.js
+++ b/addons/web/static/src/js/fields/basic_fields.js
@@ -994,6 +994,10 @@ var FieldPhone = FieldEmail.extend({
} else {
this.$el.removeClass('o_form_uri');
}
+ // This class should technically be there in case of a very very long
+ // phone number, but it breaks the o_row mechanism, which is more
+ // important right now.
+ this.$el.removeClass('o_text_overflow');
},
/**
diff --git a/addons/web/static/src/js/views/calendar/calendar_controller.js b/addons/web/static/src/js/views/calendar/calendar_controller.js
index db2394ddd1d..8d27acb43dd 100644
--- a/addons/web/static/src/js/views/calendar/calendar_controller.js
+++ b/addons/web/static/src/js/views/calendar/calendar_controller.js
@@ -28,6 +28,7 @@ var CalendarController = AbstractController.extend({
changeDate: '_onChangeDate',
changeFilter: '_onChangeFilter',
toggleFullWidth: '_onToggleFullWidth',
+ viewUpdated: '_onViewUpdated',
}),
/**
* @override
@@ -39,6 +40,7 @@ var CalendarController = AbstractController.extend({
init: function (parent, model, renderer, params) {
this._super.apply(this, arguments);
this.current_start = null;
+ this.displayName = params.displayName;
this.quickAddPop = params.quickAddPop;
this.disableQuickCreate = params.disableQuickCreate;
this.eventOpenPopup = params.eventOpenPopup;
@@ -317,6 +319,21 @@ var CalendarController = AbstractController.extend({
_onUpdateRecord: function (event) {
this._updateRecord(event.data);
},
+ /**
+ * The internal state of the calendar (mode, period displayed) has changed,
+ * so update the control panel buttons and breadcrumbs accordingly.
+ *
+ * @param {OdooEvent} event
+ */
+ _onViewUpdated: function (event) {
+ this.mode = event.data.mode;
+ if (this.$buttons) {
+ this.$buttons.find('.active').removeClass('active');
+ this.$buttons.find('.o_calendar_button_' + this.mode).addClass('active');
+ }
+ var subtitle = (this.mode === 'week' ? _t('Week ') : '') + event.data.title;
+ this.set({title: this.displayName + ' (' + subtitle + ')'});
+ },
});
return CalendarController;
diff --git a/addons/web/static/src/js/views/calendar/calendar_renderer.js b/addons/web/static/src/js/views/calendar/calendar_renderer.js
index 584813cba5a..f011a24a32e 100644
--- a/addons/web/static/src/js/views/calendar/calendar_renderer.js
+++ b/addons/web/static/src/js/views/calendar/calendar_renderer.js
@@ -354,9 +354,19 @@ return AbstractRenderer.extend({
element.find('.fc-content .fc-time').text(display_hour);
},
// Dirty hack to ensure a correct first render
- eventAfterAllRender: function (view) {
+ eventAfterAllRender: function () {
window.dispatchEvent(new Event('resize'));
},
+ viewRender: function (view) {
+ // compute mode from view.name which is either 'month', 'agendaWeek' or 'agendaDay'
+ var mode = view.name === 'month' ? 'month' : (view.name === 'agendaWeek' ? 'week' : 'day');
+ // compute title: in week mode, display the week number
+ var title = mode === 'week' ? view.intervalStart.week() : view.title;
+ self.trigger_up('viewUpdated', {
+ mode: mode,
+ title: title,
+ });
+ },
height: 'parent',
unselectAuto: false,
});
diff --git a/addons/web/static/src/js/views/calendar/calendar_view.js b/addons/web/static/src/js/views/calendar/calendar_view.js
index 47c16300508..7f58ca55632 100644
--- a/addons/web/static/src/js/views/calendar/calendar_view.js
+++ b/addons/web/static/src/js/views/calendar/calendar_view.js
@@ -109,7 +109,7 @@ var CalendarView = AbstractView.extend({
//if quick_add = False, we don't allow quick_add
//if quick_add = not specified in view, we use the default widgets.QuickCreate
- //if quick_add = is NOT False and IS specified in view, we this one for widgets.QuickCreate'
+ //if quick_add = is NOT False and IS specified in view, we this one for widgets.QuickCreate'
this.controllerParams.quickAddPop = (!('quick_add' in attrs) || utils.toBoolElse(attrs.quick_add+'', true));
this.controllerParams.disableQuickCreate = params.disable_quick_create || !this.controllerParams.quickAddPop;
// If this field is set ot true, we don't open the event in form view, but in a popup with the view_id passed by this parameter
@@ -118,6 +118,7 @@ var CalendarView = AbstractView.extend({
this.controllerParams.eventOpenPopup = utils.toBoolElse(attrs.event_open_popup || '', false);
this.controllerParams.mapping = mapping;
this.controllerParams.context = params.context || {};
+ this.controllerParams.displayName = params.action && params.action.name;
this.rendererParams.displayFields = displayFields;
this.rendererParams.eventTemplate = _.findWhere(arch.children, {'tag': 'templates'});
diff --git a/addons/web/static/src/js/views/list/list_editable_renderer.js b/addons/web/static/src/js/views/list/list_editable_renderer.js
index 06058069ab6..5bd24044c39 100644
--- a/addons/web/static/src/js/views/list/list_editable_renderer.js
+++ b/addons/web/static/src/js/views/list/list_editable_renderer.js
@@ -729,6 +729,9 @@ ListRenderer.include({
this._moveToNextLine();
break;
case 'cancel':
+ // stop the original event (typically an ESCAPE keydown), to
+ // prevent from closing the potential dialog containing this list
+ ev.data.originalEvent.stopPropagation();
this.trigger_up('discard_changes', {
recordID: ev.target.dataPointID,
});
diff --git a/addons/web/static/src/less/fields_extra.less b/addons/web/static/src/less/fields_extra.less
index 79d9628116d..541fef8ba47 100644
--- a/addons/web/static/src/less/fields_extra.less
+++ b/addons/web/static/src/less/fields_extra.less
@@ -6,7 +6,7 @@
// Required
.o_required_modifier {
&.o_input, .o_input {
- background-color: #D2D2FF;
+ background-color: #D2D2FF!important;
}
}
diff --git a/addons/web/static/tests/fields/basic_fields_tests.js b/addons/web/static/tests/fields/basic_fields_tests.js
index b040f5ebbcd..f01f2d8b589 100644
--- a/addons/web/static/tests/fields/basic_fields_tests.js
+++ b/addons/web/static/tests/fields/basic_fields_tests.js
@@ -2605,7 +2605,7 @@ QUnit.module('basic_fields', {
},
});
- var $phoneLink = form.$('a.o_form_uri.o_field_widget.o_text_overflow');
+ var $phoneLink = form.$('a.o_form_uri.o_field_widget');
assert.strictEqual($phoneLink.length, 1,
"should have a anchor with correct classes");
assert.strictEqual($phoneLink.text(), 'y\u00ADop',
@@ -2625,7 +2625,7 @@ QUnit.module('basic_fields', {
// save
form.$buttons.find('.o_form_button_save').click();
- $phoneLink = form.$('a.o_form_uri.o_field_widget.o_text_overflow');
+ $phoneLink = form.$('a.o_form_uri.o_field_widget');
assert.strictEqual($phoneLink.text(), 'n\u00ADew',
"new value should be displayed properly as text with the skype obfuscation");
assert.strictEqual($phoneLink.attr('href'), 'tel:new',
@@ -2655,7 +2655,7 @@ QUnit.module('basic_fields', {
assert.strictEqual(list.$('tbody td:not(.o_list_record_selector)').first().text(), 'y\u00ADop',
"value should be displayed properly as text with the skype obfuscation");
- var $phoneLink = list.$('a.o_form_uri.o_field_widget.o_text_overflow');
+ var $phoneLink = list.$('a.o_form_uri.o_field_widget');
assert.strictEqual($phoneLink.length, 5,
"should have anchors with correct classes");
assert.strictEqual($phoneLink.first().attr('href'), 'tel:yop',
@@ -2675,7 +2675,7 @@ QUnit.module('basic_fields', {
assert.ok(!$cell.parent().hasClass('o_selected_row'), 'should not be in edit mode anymore');
assert.strictEqual(list.$('tbody td:not(.o_list_record_selector)').first().text(), 'n\u00ADew',
"value should be properly updated");
- $phoneLink = list.$('a.o_form_uri.o_field_widget.o_text_overflow');
+ $phoneLink = list.$('a.o_form_uri.o_field_widget');
assert.strictEqual($phoneLink.length, 5,
"should still have anchors with correct classes");
assert.strictEqual($phoneLink.first().attr('href'), 'tel:new',
@@ -2715,7 +2715,7 @@ QUnit.module('basic_fields', {
},
});
- var $phone = form.$('span.o_field_widget.o_text_overflow:not(.o_form_uri)');
+ var $phone = form.$('span.o_field_widget:not(.o_form_uri)');
assert.strictEqual($phone.length, 1,
"should have a simple span rather than a link");
assert.strictEqual($phone.text(), 'yop',
@@ -2733,7 +2733,7 @@ QUnit.module('basic_fields', {
// save
form.$buttons.find('.o_form_button_save').click();
- assert.strictEqual(form.$('span.o_field_widget.o_text_overflow:not(.o_form_uri)').text(), 'new',
+ assert.strictEqual(form.$('span.o_field_widget:not(.o_form_uri)').text(), 'new',
"new value should be displayed properly as text without skype obfuscation");
form.destroy();
@@ -2768,7 +2768,7 @@ QUnit.module('basic_fields', {
assert.strictEqual(list.$('tbody td:not(.o_list_record_selector)').first().text(), 'yop',
"value should be displayed properly as text without skype obfuscation");
- assert.strictEqual(list.$('span.o_field_widget.o_text_overflow:not(.o_form_uri)').length, 5,
+ assert.strictEqual(list.$('span.o_field_widget:not(.o_form_uri)').length, 5,
"should have spans with correct classes");
// Edit a line and check the result
@@ -2785,7 +2785,7 @@ QUnit.module('basic_fields', {
assert.ok(!$cell.parent().hasClass('o_selected_row'), 'should not be in edit mode anymore');
assert.strictEqual(list.$('tbody td:not(.o_list_record_selector)').first().text(), 'new',
"value should be properly updated");
- assert.strictEqual(list.$('span.o_field_widget.o_text_overflow:not(.o_form_uri)').length, 5,
+ assert.strictEqual(list.$('span.o_field_widget:not(.o_form_uri)').length, 5,
"should still have spans with correct classes");
list.destroy();
diff --git a/addons/web/static/tests/fields/relational_fields_tests.js b/addons/web/static/tests/fields/relational_fields_tests.js
index 9b1dd8d8f61..34442ad0d50 100644
--- a/addons/web/static/tests/fields/relational_fields_tests.js
+++ b/addons/web/static/tests/fields/relational_fields_tests.js
@@ -3206,6 +3206,54 @@ QUnit.module('relational_fields', {
form.destroy();
});
+ QUnit.test('pressing escape in editable o2m list in dialog', function (assert) {
+ assert.expect(3);
+
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch: '
',
+ res_id: 1,
+ archs: {
+ "partner,false,form": '
',
+ },
+ viewOptions: {
+ mode: 'edit',
+ },
+ });
+
+ form.$('.o_field_x2many_list_row_add a').click();
+ $('.modal .o_field_x2many_list_row_add a').click();
+
+ assert.strictEqual($('.modal .o_data_row.o_selected_row').length, 1,
+ "there should be a row in edition in the dialog");
+
+ // trigger keydown ESCAPE in the edited row
+ $('.modal .o_data_cell input').trigger({type: 'keydown', which: $.ui.keyCode.ESCAPE});
+
+ assert.strictEqual($('.modal').length, 1,
+ "dialog should still be open");
+ assert.strictEqual($('.modal .o_data_row').length, 0,
+ "the row should have been removed");
+
+ form.destroy();
+ });
+
QUnit.test('editable o2m with onchange and required field: delete an invalid line', function (assert) {
assert.expect(5);
@@ -5954,7 +6002,6 @@ QUnit.module('relational_fields', {
form.destroy();
});
-
QUnit.test('editing tabbed one2many (editable=bottom)', function (assert) {
assert.expect(12);
diff --git a/addons/website_blog/controllers/main.py b/addons/website_blog/controllers/main.py
index a7ac33d7997..6ebb67c8782 100644
--- a/addons/website_blog/controllers/main.py
+++ b/addons/website_blog/controllers/main.py
@@ -35,7 +35,7 @@ class WebsiteBlog(http.Controller):
group['date_begin'] = start
group['date_end'] = end
- locale = request.context.get('lang', 'en_US')
+ locale = request.context.get('lang') or 'en_US'
start = pytz.UTC.localize(fields.Datetime.from_string(start))
tzinfo = pytz.timezone(request.context.get('tz', 'utc') or 'utc')
diff --git a/addons/website_event/controllers/main.py b/addons/website_event/controllers/main.py
index 28ac0c17a8a..4f5a6516720 100644
--- a/addons/website_event/controllers/main.py
+++ b/addons/website_event/controllers/main.py
@@ -197,7 +197,7 @@ class WebsiteEventController(http.Controller):
def get_formated_date(self, event):
start_date = fields.Datetime.from_string(event.date_begin).date()
end_date = fields.Datetime.from_string(event.date_end).date()
- month = babel.dates.get_month_names('abbreviated', locale=event.env.context.get('lang', 'en_US'))[start_date.month]
+ month = babel.dates.get_month_names('abbreviated', locale=event.env.context.get('lang') or 'en_US')[start_date.month]
return ('%s %s%s') % (month, start_date.strftime("%e"), (end_date != start_date and ("-" + end_date.strftime("%e")) or ""))
@http.route('/event/get_country_event_list', type='json', auth='public', website=True)
diff --git a/addons/website_event_track/views/event_track_templates.xml b/addons/website_event_track/views/event_track_templates.xml
index caf9d7d68e2..d8d94f18026 100644
--- a/addons/website_event_track/views/event_track_templates.xml
+++ b/addons/website_event_track/views/event_track_templates.xml
@@ -220,11 +220,11 @@