diff --git a/addons/account_analytic_default/__manifest__.py b/addons/account_analytic_default/__manifest__.py
index 283e004fd20..3559efb4261 100644
--- a/addons/account_analytic_default/__manifest__.py
+++ b/addons/account_analytic_default/__manifest__.py
@@ -23,7 +23,6 @@ Allows to automatically select analytic accounts based on criterions:
'security/ir.model.access.csv',
'security/account_analytic_default_security.xml',
'views/account_analytic_default_view.xml',
- 'views/product_views.xml',
'views/res_config_settings_views.xml',
],
'installable': True,
diff --git a/addons/account_analytic_default/models/__init__.py b/addons/account_analytic_default/models/__init__.py
index e3b0fea9a16..1df432cd768 100644
--- a/addons/account_analytic_default/models/__init__.py
+++ b/addons/account_analytic_default/models/__init__.py
@@ -3,4 +3,3 @@
from . import account_analytic_default
from . import sale_order_line
-from . import product
diff --git a/addons/account_analytic_default/models/product.py b/addons/account_analytic_default/models/product.py
deleted file mode 100644
index 0fcb61360e1..00000000000
--- a/addons/account_analytic_default/models/product.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from odoo import api, fields, models
-
-
-class ProductProduct(models.Model):
- _inherit = 'product.product'
-
- rules_count = fields.Integer(compute='_compute_rules_count', string='# Analytic Rules')
-
- def _compute_rules_count(self):
- Analytic = self.env['account.analytic.default']
- for product in self:
- product.rules_count = Analytic.search_count([('product_id', '=', product.id)])
-
-
-class ProductTemplate(models.Model):
- _inherit = 'product.template'
-
- rules_count = fields.Integer(compute='_compute_rules_count', string='# Analytic Rules')
-
- def _compute_rules_count(self):
- for template in self:
- template.rules_count = sum([p.rules_count for p in template.product_variant_ids])
-
- @api.multi
- def action_view_rules(self):
- result = self.env.ref('account_analytic_default.action_product_default_list').read()[0]
- result['domain'] = [('product_id', 'in', self.mapped('product_variant_ids').ids)]
- # Remove context so it is not going to filter on product_id with active_id of template
- result['context'] = {'default_product_id': self.mapped('product_variant_ids').id}
- return result
diff --git a/addons/account_analytic_default/views/product_views.xml b/addons/account_analytic_default/views/product_views.xml
deleted file mode 100644
index 790b45d36d6..00000000000
--- a/addons/account_analytic_default/views/product_views.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
- product.product.inherit.rules_count
- product.product
-
-
-
-
-
-
-
- product.template.inherit.rules_count
- product.template
-
-
-
-
-
-
-
-
-
-
diff --git a/addons/auth_signup/models/res_config_settings.py b/addons/auth_signup/models/res_config_settings.py
index d4c31ffe124..93436c80fab 100644
--- a/addons/auth_signup/models/res_config_settings.py
+++ b/addons/auth_signup/models/res_config_settings.py
@@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
+from ast import literal_eval
+
from odoo import api, fields, models
@@ -22,7 +24,7 @@ class ResConfigSettings(models.TransientModel):
res.update(
auth_signup_reset_password=get_param('auth_signup.reset_password', 'False').lower() == 'true',
auth_signup_uninvited='b2c' if get_param('auth_signup.allow_uninvited', 'False').lower() == 'true' else 'b2b',
- auth_signup_template_user_id=get_param('auth_signup.template_user_id', 'False').lower() == 'true',
+ auth_signup_template_user_id=literal_eval(get_param('auth_signup.template_user_id', 'False')),
)
return res
@@ -38,6 +40,6 @@ class ResConfigSettings(models.TransientModel):
@api.multi
def open_template_user(self):
action = self.env.ref('base.action_res_users').read()[0]
- action['res_id'] = self.env.ref('auth_signup.default_template_user').id
+ action['res_id'] = literal_eval(self.env['ir.config_parameter'].sudo().get_param('auth_signup.template_user_id', 'False'))
action['views'] = [[self.env.ref('base.view_users_form').id, 'form']]
return action
diff --git a/addons/auth_signup/views/res_config_settings_views.xml b/addons/auth_signup/views/res_config_settings_views.xml
index bcabc3665ee..27489908dcb 100644
--- a/addons/auth_signup/views/res_config_settings_views.xml
+++ b/addons/auth_signup/views/res_config_settings_views.xml
@@ -19,7 +19,11 @@
-
+
+ Copy access rights from
+
+
diff --git a/addons/base_import/models/base_import.py b/addons/base_import/models/base_import.py
index 40324f194dc..720cdc1e71a 100644
--- a/addons/base_import/models/base_import.py
+++ b/addons/base_import/models/base_import.py
@@ -297,6 +297,8 @@ class Import(models.TransientModel):
if all(val.lower() in ('true', 'false', 't', 'f', '') for val in preview_values):
return ['boolean']
# If all values can be cast to float, type is either float or monetary
+ # Or a date/datetime if it matches the pattern
+ results = []
try:
thousand_separator = decimal_separator = False
for val in preview_values:
@@ -328,7 +330,7 @@ class Import(models.TransientModel):
if thousand_separator and not options.get('float_decimal_separator'):
options['float_thousand_separator'] = thousand_separator
options['float_decimal_separator'] = decimal_separator
- return ['float', 'monetary']
+ results = ['float', 'monetary']
except ValueError:
pass
# Try to see if all values are a date or datetime
@@ -364,13 +366,15 @@ class Import(models.TransientModel):
current_date_pattern = check_patterns(date_patterns, preview_values)
if current_date_pattern:
options['date_format'] = current_date_pattern
- return ['date']
+ results += ['date']
current_datetime_pattern = check_patterns(datetime_patterns, preview_values)
if current_datetime_pattern:
options['datetime_format'] = current_datetime_pattern
- return ['datetime']
+ results += ['datetime']
+ if results:
+ return results
return ['id', 'text', 'char', 'datetime', 'selection', 'many2one', 'one2many', 'many2many', 'html']
@api.model
@@ -492,7 +496,7 @@ class Import(models.TransientModel):
'headers_type': header_types or False,
'preview': preview,
'options': options,
- 'advanced_mode': any([len(models.fix_import_export_id_paths(col)) > 1 for col in headers]),
+ 'advanced_mode': any([len(models.fix_import_export_id_paths(col)) > 1 for col in headers or []]),
'debug': self.user_has_groups('base.group_no_one'),
}
except Exception as error:
diff --git a/addons/board/controllers/main.py b/addons/board/controllers/main.py
index ebd9b568583..96f1aad1a0a 100644
--- a/addons/board/controllers/main.py
+++ b/addons/board/controllers/main.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from xml.etree import ElementTree
+from lxml import etree as ElementTree
from odoo.http import Controller, route, request
diff --git a/addons/delivery/models/delivery_carrier.py b/addons/delivery/models/delivery_carrier.py
index 4c1ecff878d..e4e20099474 100644
--- a/addons/delivery/models/delivery_carrier.py
+++ b/addons/delivery/models/delivery_carrier.py
@@ -87,9 +87,9 @@ class DeliveryCarrier(models.Model):
return False
if self.state_ids and partner.state_id not in self.state_ids:
return False
- if self.zip_from and (partner.zip or '') < self.zip_from:
+ if self.zip_from and (partner.zip or '').upper() < self.zip_from.upper():
return False
- if self.zip_to and (partner.zip or '') > self.zip_to:
+ if self.zip_to and (partner.zip or '').upper() > self.zip_to.upper():
return False
return True
diff --git a/addons/im_livechat/controllers/main.py b/addons/im_livechat/controllers/main.py
index 35414321bed..a79c6f4dc80 100644
--- a/addons/im_livechat/controllers/main.py
+++ b/addons/im_livechat/controllers/main.py
@@ -75,7 +75,7 @@ class LivechatController(http.Controller):
# if the user is identifiy (eg: portal user on the frontend), don't use the anonymous name. The user will be added to session.
if request.session.uid:
anonymous_name = request.env.user.name
- return request.env["im_livechat.channel"].get_mail_channel(channel_id, anonymous_name)
+ return request.env["im_livechat.channel"].with_context(lang=False).get_mail_channel(channel_id, anonymous_name)
@http.route('/im_livechat/feedback', type='json', auth='public')
def feedback(self, uuid, rate, reason=None, **kwargs):
diff --git a/addons/mail/static/src/js/systray.js b/addons/mail/static/src/js/systray.js
index d06ed5894c6..5995c635f33 100644
--- a/addons/mail/static/src/js/systray.js
+++ b/addons/mail/static/src/js/systray.js
@@ -162,7 +162,7 @@ var ActivityMenu = Widget.extend({
template:'mail.chat.ActivityMenu',
events: {
"click": "_onActivityMenuClick",
- "click .o_activity_filter_button, .o_mail_channel_preview": "_onActivityFilterClick",
+ "click .o_mail_channel_preview": "_onActivityFilterClick",
},
start: function () {
this.$activities_preview = this.$('.o_mail_navbar_dropdown_channels');
@@ -252,19 +252,19 @@ var ActivityMenu = Widget.extend({
* @param {MouseEvent} event
*/
_onActivityFilterClick: function (event) {
- event.stopPropagation();
- var $target = $(event.currentTarget);
+ // fetch the data from the button otherwise fetch the ones from the parent (.o_mail_channel_preview).
+ var data = _.extend({}, $(event.currentTarget).data(), $(event.target).data());
var context = {};
- if ($target.data('filter')=='my') {
+ if (data.filter === 'my') {
context['search_default_activities_overdue'] = 1;
context['search_default_activities_today'] = 1;
} else {
- context['search_default_activities_' + $target.data('filter')] = 1;
+ context['search_default_activities_' + data.filter] = 1;
}
this.do_action({
type: 'ir.actions.act_window',
- name: $target.data('model_name'),
- res_model: $target.data('res_model'),
+ name: data.model_name,
+ res_model: data.res_model,
views: [[false, 'kanban'], [false, 'form']],
search_view_id: [false],
domain: [['activity_user_id', '=', session.uid]],
diff --git a/addons/mail/static/tests/systray_tests.js b/addons/mail/static/tests/systray_tests.js
index 7368bc59859..60693ea1dee 100644
--- a/addons/mail/static/tests/systray_tests.js
+++ b/addons/mail/static/tests/systray_tests.js
@@ -41,8 +41,8 @@ QUnit.module('ActivityMenu', {
res_model: "project.issue",
planned_count: 1,
today_count: 1,
- overdue_count: 0,
- total_count: 2,
+ overdue_count: 1,
+ total_count: 3,
}],
},
};
@@ -67,10 +67,9 @@ QUnit.test('activity menu widget: menu with no records', function (assert) {
});
QUnit.test('activity menu widget: activity menu with 3 records', function (assert) {
- assert.expect(4);
+ assert.expect(10);
var self = this;
var activityMenu = new systray.ActivityMenu();
-
testUtils.addMockEnvironment(activityMenu, {
mockRPC: function (route, args) {
if (args.method === 'activity_user_count') {
@@ -83,7 +82,41 @@ QUnit.test('activity menu widget: activity menu with 3 records', function (asser
assert.ok(activityMenu.$el.hasClass('o_mail_navbar_item'), 'should be the instance of widget');
assert.ok(activityMenu.$('.o_mail_channel_preview').hasClass('o_mail_channel_preview'), "should instance of widget");
assert.ok(activityMenu.$('.o_notification_counter').hasClass('o_notification_counter'), "widget should have notification counter");
- assert.strictEqual(parseInt(activityMenu.el.innerText), 4, "widget should have 4 notification counter");
+ assert.strictEqual(parseInt(activityMenu.el.innerText), 5, "widget should have 5 notification counter");
+
+ var context = {};
+ testUtils.intercept(activityMenu, 'do_action', function(event) {
+ assert.deepEqual(event.data.action.context, context, "wrong context value");
+ }, true);
+
+ // case 1: click on "late"
+ context = {
+ search_default_activities_overdue: 1,
+ };
+ activityMenu.$('.dropdown-toggle').click();
+ assert.strictEqual(activityMenu.$el.hasClass("open"), true, 'ActivityMenu should be open');
+ activityMenu.$(".o_activity_filter_button[data-model_name='Issue'][data-filter='overdue']").click();
+ assert.strictEqual(activityMenu.$el.hasClass("open"), false, 'ActivityMenu should be closed');
+ // case 2: click on "today"
+ context = {
+ search_default_activities_today: 1,
+ };
+ activityMenu.$('.dropdown-toggle').click();
+ activityMenu.$(".o_activity_filter_button[data-model_name='Issue'][data-filter='today']").click();
+ // case 3: click on "future"
+ context = {
+ search_default_activities_upcoming_all: 1,
+ };
+ activityMenu.$('.dropdown-toggle').click();
+ activityMenu.$(".o_activity_filter_button[data-model_name='Issue'][data-filter='upcoming_all']").click();
+ // case 4: click anywere else
+ context = {
+ search_default_activities_overdue: 1,
+ search_default_activities_today: 1,
+ };
+ activityMenu.$('.dropdown-toggle').click();
+ activityMenu.$(".o_mail_navbar_dropdown_channels > div[data-model_name='Issue']").click();
+
activityMenu.destroy();
});
});
diff --git a/addons/mrp/data/mrp_lot_demo.yml b/addons/mrp/data/mrp_lot_demo.yml
index 0f5c5c987d2..bb0c54e238e 100644
--- a/addons/mrp/data/mrp_lot_demo.yml
+++ b/addons/mrp/data/mrp_lot_demo.yml
@@ -5,7 +5,7 @@
wiz_obj = self.env['mrp.product.produce']
ctx = dict(self.env.context, active_id=self.id)
produce_wiz = wiz_obj.with_context(ctx).create({'product_qty': self.product_qty, 'lot_id': ref('lot_product_27_0')})
- produce_wiz.consume_line_ids.write({'qty_done': produce_wiz.product_qty})
+ produce_wiz.produce_line_ids.write({'qty_done': produce_wiz.product_qty})
#Produce Product,Post Inventory and Set MO Done.
produce_wiz.do_produce()
self.post_inventory()
diff --git a/addons/mrp/models/mrp_workorder.py b/addons/mrp/models/mrp_workorder.py
index 4e8244da860..89a89fcaf55 100644
--- a/addons/mrp/models/mrp_workorder.py
+++ b/addons/mrp/models/mrp_workorder.py
@@ -109,7 +109,7 @@ class MrpWorkorder(models.Model):
'stock.move.line', 'workorder_id',
domain=[('done_wo', '=', False)])
final_lot_id = fields.Many2one(
- 'stock.production.lot', 'Current Lot', domain="[('product_id', '=', product_id)]",
+ 'stock.production.lot', 'Lot/Serial Number', domain="[('product_id', '=', product_id)]",
states={'done': [('readonly', True)], 'cancel': [('readonly', True)]})
time_ids = fields.One2many(
'mrp.workcenter.productivity', 'workorder_id')
diff --git a/addons/mrp/tests/test_unbuild.py b/addons/mrp/tests/test_unbuild.py
index 1a184895c93..f035f2e31b9 100644
--- a/addons/mrp/tests/test_unbuild.py
+++ b/addons/mrp/tests/test_unbuild.py
@@ -113,7 +113,7 @@ class TestUnbuild(TestMrpCommon):
}).action_unbuild()
# Check quantity in stock after last unbuild.
- self.assertEqual(self.env['stock.quant']._get_available_quantity(p_final, self.stock_location), -5, 'You should have negative quantity for final product in stock')
+ self.assertEqual(self.env['stock.quant']._get_available_quantity(p_final, self.stock_location, allow_negative=True), -5, 'You should have negative quantity for final product in stock')
self.assertEqual(self.env['stock.quant']._get_available_quantity(p1, self.stock_location), 120, 'You should have 80 products in stock')
self.assertEqual(self.env['stock.quant']._get_available_quantity(p2, self.stock_location), 10, 'You should have consumed all the 5 product in stock')
@@ -194,7 +194,7 @@ class TestUnbuild(TestMrpCommon):
'product_uom_id': self.uom_unit.id,
}).action_unbuild()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(p_final, self.stock_location, lot_id=lot), -5, 'You should have negative quantity for final product in stock')
+ self.assertEqual(self.env['stock.quant']._get_available_quantity(p_final, self.stock_location, lot_id=lot, allow_negative=True), -5, 'You should have negative quantity for final product in stock')
self.assertEqual(self.env['stock.quant']._get_available_quantity(p1, self.stock_location), 120, 'You should have 80 products in stock')
self.assertEqual(self.env['stock.quant']._get_available_quantity(p2, self.stock_location), 10, 'You should have consumed all the 5 product in stock')
@@ -279,7 +279,7 @@ class TestUnbuild(TestMrpCommon):
'product_uom_id': self.uom_unit.id,
}).action_unbuild()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(p_final, self.stock_location), -5, 'You should have negative quantity for final product in stock')
+ self.assertEqual(self.env['stock.quant']._get_available_quantity(p_final, self.stock_location, allow_negative=True), -5, 'You should have negative quantity for final product in stock')
self.assertEqual(self.env['stock.quant']._get_available_quantity(p1, self.stock_location, lot_id=lot), 120, 'You should have 80 products in stock')
self.assertEqual(self.env['stock.quant']._get_available_quantity(p2, self.stock_location), 10, 'You should have consumed all the 5 product in stock')
@@ -378,7 +378,7 @@ class TestUnbuild(TestMrpCommon):
'product_uom_id': self.uom_unit.id,
}).action_unbuild()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(p_final, self.stock_location, lot_id=lot_final), -5, 'You should have negative quantity for final product in stock')
+ self.assertEqual(self.env['stock.quant']._get_available_quantity(p_final, self.stock_location, lot_id=lot_final, allow_negative=True), -5, 'You should have negative quantity for final product in stock')
self.assertEqual(self.env['stock.quant']._get_available_quantity(p1, self.stock_location, lot_id=lot_1), 120, 'You should have 80 products in stock')
self.assertEqual(self.env['stock.quant']._get_available_quantity(p2, self.stock_location, lot_id=lot_2), 10, 'You should have consumed all the 5 product in stock')
diff --git a/addons/mrp/tests/test_workorder_operation.py b/addons/mrp/tests/test_workorder_operation.py
index bbbd8efd08d..a52332c05a0 100644
--- a/addons/mrp/tests/test_workorder_operation.py
+++ b/addons/mrp/tests/test_workorder_operation.py
@@ -229,12 +229,11 @@ class TestWorkOrderProcess(common.TransactionCase):
# -----------------
# Produce 6 Unit of custom laptop will consume ( 12 Unit of keybord and 12 Unit of charger)
-
context = {"active_ids": [mo_custom_laptop.id], "active_id": mo_custom_laptop.id}
product_consume = self.env['mrp.product.produce'].with_context(context).create({'product_qty': 6.00})
laptop_lot_001 = self.env['stock.production.lot'].create({'product_id': custom_laptop.id})
product_consume.lot_id = laptop_lot_001.id
- product_consume.consume_line_ids.write({'qty_done': 12})
+ product_consume.produce_line_ids.write({'qty_done': 12})
product_consume.do_produce()
# Check consumed move after produce 6 quantity of customized laptop.
@@ -259,8 +258,8 @@ class TestWorkOrderProcess(common.TransactionCase):
product_consume = self.env['mrp.product.produce'].with_context(context).create({'product_qty': 4.00})
laptop_lot_002 = self.env['stock.production.lot'].create({'product_id': custom_laptop.id})
product_consume.lot_id = laptop_lot_002.id
- self.assertEquals(len(product_consume.consume_line_ids), 2)
- product_consume.consume_line_ids.write({'qty_done': 8})
+ self.assertEquals(len(product_consume.produce_line_ids), 2)
+ product_consume.produce_line_ids.write({'qty_done': 8})
product_consume.do_produce()
charger_move = mo_custom_laptop.move_raw_ids.filtered(lambda x: x.product_id.id == product_charger.id and x.state != 'done')
keybord_move = mo_custom_laptop.move_raw_ids.filtered(lambda x: x.product_id.id == product_keybord.id and x.state !='done')
@@ -394,9 +393,9 @@ class TestWorkOrderProcess(common.TransactionCase):
product_consume = self.env['mrp.product.produce'].with_context(context).create({'product_qty': 10})
# laptop_lot_002 = self.env['stock.production.lot'].create({'product_id': custom_laptop.id})
product_consume.lot_id = lot_a.id
- self.assertEquals(len(product_consume.consume_line_ids), 2)
- product_consume.consume_line_ids.filtered(lambda x : x.product_id == product_C).write({'qty_done': 3000})
- product_consume.consume_line_ids.filtered(lambda x : x.product_id == product_B).write({'qty_done': 20})
+ self.assertEquals(len(product_consume.produce_line_ids), 2)
+ product_consume.produce_line_ids.filtered(lambda x : x.product_id == product_C).write({'qty_done': 3000})
+ product_consume.produce_line_ids.filtered(lambda x : x.product_id == product_B).write({'qty_done': 20})
product_consume.do_produce()
mo_custom_product.post_inventory()
diff --git a/addons/mrp/views/mrp_production_views.xml b/addons/mrp/views/mrp_production_views.xml
index 2b9e5dcf856..3dbc4effaa4 100644
--- a/addons/mrp/views/mrp_production_views.xml
+++ b/addons/mrp/views/mrp_production_views.xml
@@ -59,7 +59,7 @@
Work Orders
-