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
- -
@@ -268,7 +272,7 @@ Forecasted -