diff --git a/addons/hr_attendance/static/src/js/kiosk_mode.js b/addons/hr_attendance/static/src/js/kiosk_mode.js
index d561f380c45..234df40fe07 100644
--- a/addons/hr_attendance/static/src/js/kiosk_mode.js
+++ b/addons/hr_attendance/static/src/js/kiosk_mode.js
@@ -1,6 +1,7 @@
odoo.define('hr_attendance.kiosk_mode', function (require) {
"use strict";
+var ajax = require('web.ajax');
var core = require('web.core');
var Widget = require('web.Widget');
var Session = require('web.session');
@@ -27,6 +28,8 @@ var KioskMode = Widget.extend({
self.$el.html(QWeb.render("HrAttendanceKioskMode", {widget: self}));
self.start_clock();
});
+ // Make a RPC call every day to keep the session alive
+ self._interval = window.setInterval(this._callServer.bind(this), (60*60*1000*24));
return $.when(def, this._super.apply(this, arguments));
},
@@ -55,8 +58,15 @@ var KioskMode = Widget.extend({
destroy: function () {
core.bus.off('barcode_scanned', this, this._onBarcodeScanned);
clearInterval(this.clock_start);
+ clearInterval(this._interval);
this._super.apply(this, arguments);
},
+
+ _callServer: function () {
+ // Make a call to the database to avoid the auto close of the session
+ return ajax.rpc("/web/webclient/version_info", {});
+ },
+
});
core.action_registry.add('hr_attendance_kiosk_mode', KioskMode);
diff --git a/addons/iap/__manifest__.py b/addons/iap/__manifest__.py
index 35124df76eb..6a30ed891c9 100644
--- a/addons/iap/__manifest__.py
+++ b/addons/iap/__manifest__.py
@@ -9,7 +9,7 @@
'description': """
This module provides standard tools (account model, context manager and helpers) to support In-App purchases inside Odoo.
""",
- 'depends': ['web'],
+ 'depends': ['web', 'web_settings_dashboard'],
'data': [
'security/ir.model.access.csv',
'security/ir_rule.xml',
diff --git a/addons/purchase/report/purchase_report.py b/addons/purchase/report/purchase_report.py
index 95ddfe49bc5..603dc9645f2 100644
--- a/addons/purchase/report/purchase_report.py
+++ b/addons/purchase/report/purchase_report.py
@@ -88,7 +88,7 @@ class PurchaseReport(models.Model):
join res_partner partner on s.partner_id = partner.id
left join product_product p on (l.product_id=p.id)
left join product_template t on (p.product_tmpl_id=t.id)
- LEFT JOIN ir_property ip ON (ip.name='standard_price' AND ip.res_id=CONCAT('product.template,',t.id) AND ip.company_id=s.company_id)
+ LEFT JOIN ir_property ip ON (ip.name='standard_price' AND ip.res_id=CONCAT('product.product,',p.id) AND ip.company_id=s.company_id)
left join product_uom u on (u.id=l.product_uom)
left join product_uom u2 on (u2.id=t.uom_id)
left join stock_picking_type spt on (spt.id=s.picking_type_id)
diff --git a/addons/web/static/src/js/views/abstract_controller.js b/addons/web/static/src/js/views/abstract_controller.js
index ff7ab5a43bb..fbddb28eab7 100644
--- a/addons/web/static/src/js/views/abstract_controller.js
+++ b/addons/web/static/src/js/views/abstract_controller.js
@@ -12,6 +12,7 @@ odoo.define('web.AbstractController', function (require) {
* reading localstorage, ...) has to go through the controller.
*/
+var concurrency = require('web.concurrency');
var ControlPanelMixin = require('web.ControlPanelMixin');
var core = require('web.core');
var Widget = require('web.Widget');
@@ -54,6 +55,8 @@ var AbstractController = Widget.extend(ControlPanelMixin, {
this.controllerID = params.controllerID;
this.initialState = params.initialState;
+ // use a DropPrevious to correctly handle concurrent updates
+ this.dp = new concurrency.DropPrevious();
// those arguments are temporary, they won't be necessary as soon as the
// ControlPanel will be handled by the View
this.displayName = params.displayName;
@@ -225,11 +228,11 @@ var AbstractController = Widget.extend(ControlPanelMixin, {
var self = this;
var shouldReload = (options && 'reload' in options) ? options.reload : true;
var def = shouldReload ? this.model.reload(this.handle, params) : $.when();
- return def.then(function (handle) {
+ return this.dp.add(def).then(function (handle) {
self.handle = handle || self.handle; // update handle if we reloaded
var state = self.model.get(self.handle);
var localState = self.renderer.getLocalState();
- return self.renderer.updateState(state, params).then(function () {
+ return self.dp.add(self.renderer.updateState(state, params)).then(function () {
self.renderer.setLocalState(localState);
self._update(state);
});
diff --git a/addons/web/static/tests/views/list_tests.js b/addons/web/static/tests/views/list_tests.js
index e322f35abb4..fd851fc55de 100644
--- a/addons/web/static/tests/views/list_tests.js
+++ b/addons/web/static/tests/views/list_tests.js
@@ -3309,7 +3309,6 @@ QUnit.module('Views', {
list.destroy();
});
-
QUnit.test('basic support for widgets', function (assert) {
assert.expect(1);
@@ -3415,6 +3414,46 @@ QUnit.module('Views', {
testUtils.unpatch(mixins.ParentedMixin);
});
+ QUnit.test('concurrent reloads finishing in inverse order', function (assert) {
+ assert.expect(3);
+
+ var blockSearchRead = false;
+ var def = $.Deferred();
+ var list = createView({
+ View: ListView,
+ model: 'foo',
+ data: this.data,
+ arch: '',
+ mockRPC: function (route) {
+ var result = this._super.apply(this, arguments);
+ if (route === '/web/dataset/search_read' && blockSearchRead) {
+ return $.when(def).then(_.constant(result));
+ }
+ return result;
+ },
+ });
+
+ assert.strictEqual(list.$('.o_list_view .o_data_row').length, 4,
+ "list view should contain 4 records");
+
+ // reload with a domain (this request is blocked)
+ blockSearchRead = true;
+ list.reload({domain: [['foo', '=', 'yop']]});
+
+ assert.strictEqual(list.$('.o_list_view .o_data_row').length, 4,
+ "list view should still contain 4 records (search_read being blocked)");
+
+ // reload without the domain
+ blockSearchRead = false;
+ list.reload({domain: []});
+
+ // unblock the RPC
+ def.resolve();
+ assert.strictEqual(list.$('.o_list_view .o_data_row').length, 4,
+ "list view should still contain 4 records");
+
+ list.destroy();
+ });
});
});
diff --git a/addons/website_quote/__manifest__.py b/addons/website_quote/__manifest__.py
index e6a0a1c274f..115f8f6821d 100644
--- a/addons/website_quote/__manifest__.py
+++ b/addons/website_quote/__manifest__.py
@@ -7,7 +7,7 @@
'website': 'https://www.odoo.com/page/quote-builder',
'version': '1.0',
'description': "",
- 'depends': ['website', 'sale_management', 'mail', 'payment', 'website_mail'],
+ 'depends': ['website', 'sale_management', 'mail', 'payment', 'website_mail', 'sale_payment'],
'data': [
'data/website_quote_data.xml',
'report/sale_order_reports.xml',
diff --git a/odoo/addons/base/models/res_partner.py b/odoo/addons/base/models/res_partner.py
index 30a4cae01b5..a9a5b36fc2e 100644
--- a/odoo/addons/base/models/res_partner.py
+++ b/odoo/addons/base/models/res_partner.py
@@ -179,7 +179,9 @@ class Partner(models.Model):
[('contact', 'Contact'),
('invoice', 'Invoice address'),
('delivery', 'Shipping address'),
- ('other', 'Other address')], string='Address Type',
+ ('other', 'Other address'),
+ ("private", "Private Address"),
+ ], string='Address Type',
default='contact',
help="Used to select automatically the right address according to the context in sales and purchases documents.")
street = fields.Char()
diff --git a/odoo/addons/base/security/base_security.xml b/odoo/addons/base/security/base_security.xml
index 8d03802aadf..f3bf2f908dd 100644
--- a/odoo/addons/base/security/base_security.xml
+++ b/odoo/addons/base/security/base_security.xml
@@ -1,5 +1,9 @@
+
+ Access to Private Addresses
+
+
res.users.log per user
@@ -69,6 +73,37 @@
['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]
+
+
+ res.partner.rule.private.employee
+
+
+ [('type', '!=', 'private')]
+
+
+
+
+
+
+
+
+
+ res.partner.rule.private.group
+
+
+ [('type', '=', 'private')]
+
+
+
+
+
+
+
+
ir.filters.admin.all.rights
diff --git a/odoo/addons/base/views/res_partner_views.xml b/odoo/addons/base/views/res_partner_views.xml
index 8893ee1cb15..3eb69146c4c 100644
--- a/odoo/addons/base/views/res_partner_views.xml
+++ b/odoo/addons/base/views/res_partner_views.xml
@@ -390,6 +390,50 @@
+
+
+ res.partner.view.form.private
+ res.partner
+
+
+
+
+
+
res.partner.select
res.partner