diff --git a/addons/product/models/res_config_settings.py b/addons/product/models/res_config_settings.py index 7e73f4df5e6..6272b1f37ba 100644 --- a/addons/product/models/res_config_settings.py +++ b/addons/product/models/res_config_settings.py @@ -14,6 +14,7 @@ class ResConfigSettings(models.TransientModel): " * Unchecked : Each company can see only its product (product where company is defined). Product not related to a company are visible for all companies.") group_uom = fields.Boolean("Units of Measure", implied_group='uom.group_uom') group_product_variant = fields.Boolean("Variants and Options", implied_group='product.group_product_variant') + module_sale_product_configurator = fields.Boolean("Product Configurator") group_stock_packaging = fields.Boolean('Product Packagings', implied_group='product.group_stock_packaging') group_sale_pricelist = fields.Boolean("Use pricelists to adapt your price per customers", @@ -33,6 +34,19 @@ class ResConfigSettings(models.TransientModel): ('1', 'Cubic Feet'), ], 'Volume unit of measure', config_parameter='product.volume_in_cubic_feet', default='0') + @api.onchange('group_product_variant') + def _onchange_group_product_variant(self): + """The product Configurator requires the product variants activated. + If the user disables the product variants -> disable the product configurator as well""" + if self.module_sale_product_configurator and not self.group_product_variant: + self.module_sale_product_configurator = False + + @api.onchange('module_sale_product_configurator') + def _onchange_module_sale_product_configurator(self): + """The product Configurator requires the product variants activated + If the user enables the product configurator -> enable the product variants as well""" + if self.module_sale_product_configurator and not self.group_product_variant: + self.group_product_variant = True @api.model def get_values(self): diff --git a/addons/sale/__manifest__.py b/addons/sale/__manifest__.py index 741d357c900..c275df0ac9b 100644 --- a/addons/sale/__manifest__.py +++ b/addons/sale/__manifest__.py @@ -27,13 +27,12 @@ This module contains all the common features of Sales Management and eCommerce. 'views/res_partner_views.xml', 'views/mail_activity_views.xml', 'views/assets.xml', + 'views/variant_templates.xml', 'views/sale_portal_templates.xml', - 'views/sale_product_configurator_templates.xml', 'views/sale_onboarding_views.xml', 'views/res_config_settings_views.xml', 'views/payment_views.xml', 'views/product_attribute_views.xml', - 'wizard/sale_product_configurator_views.xml', ], 'demo': [ 'data/sale_demo.xml', diff --git a/addons/sale/controllers/__init__.py b/addons/sale/controllers/__init__.py index ac5f1269f82..a35a880227c 100644 --- a/addons/sale/controllers/__init__.py +++ b/addons/sale/controllers/__init__.py @@ -3,4 +3,4 @@ from . import onboarding from . import portal -from . import product_configurator +from . import variant diff --git a/addons/sale/controllers/variant.py b/addons/sale/controllers/variant.py new file mode 100644 index 00000000000..3ca2c7c32b0 --- /dev/null +++ b/addons/sale/controllers/variant.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import http +from odoo.http import request + + +class VariantController(http.Controller): + @http.route(['/sale/get_combination_info'], type='json', auth="user", methods=['POST']) + def get_combination_info(self, product_template_id, product_id, combination, add_qty, pricelist_id, **kw): + combination = request.env['product.template.attribute.value'].browse(combination) + pricelist = self._get_pricelist(pricelist_id) + ProductTemplate = request.env['product.template'] + if 'context' in kw: + ProductTemplate = ProductTemplate.with_context(**kw.get('context')) + return ProductTemplate.browse(int(product_template_id))._get_combination_info(combination, int(product_id or 0), int(add_qty or 1), pricelist) + + @http.route(['/sale/create_product_variant'], type='json', auth="user", methods=['POST']) + def create_product_variant(self, product_template_id, product_template_attribute_value_ids, **kwargs): + return request.env['product.template'].browse(int(product_template_id)).create_product_variant(product_template_attribute_value_ids) + + def _get_pricelist(self, pricelist_id, pricelist_fallback=False): + return request.env['product.pricelist'].browse(int(pricelist_id or 0)) + + def _get_combination_info(self, product_template_id, product_id, combination, add_qty, pricelist, **kw): + """deprecated, use product method""" + combination = request.env['product.template.attribute.value'].browse(combination) + return request.env['product.template'].browse(product_template_id)._get_combination_info(combination, product_id, add_qty, pricelist) diff --git a/addons/sale/data/sale_demo.xml b/addons/sale/data/sale_demo.xml index d0ccf291eff..d83063b9158 100644 --- a/addons/sale/data/sale_demo.xml +++ b/addons/sale/data/sale_demo.xml @@ -730,23 +730,6 @@ Thanks! - - Chair floor protection - - 12.0 - 0.01 - - - Office chairs can harm your floor: protect it. - - - - - - - - - Custom diff --git a/addons/sale/models/product_product.py b/addons/sale/models/product_product.py index a489b03b952..8d919e39ac3 100644 --- a/addons/sale/models/product_product.py +++ b/addons/sale/models/product_product.py @@ -5,16 +5,6 @@ from odoo import api, fields, models from odoo.tools.float_utils import float_round -class ProductTemplate(models.Model): - _inherit = "product.template" - - optional_product_ids = fields.Many2many( - 'product.template', 'product_optional_rel', 'src_id', 'dest_id', - string='Optional Products', help="Optional Products are suggested " - "whenever the customer hits *Add to Cart* (cross-sell strategy, " - "e.g. for computers: warranty, software, etc.).") - - class ProductProduct(models.Model): _inherit = 'product.product' diff --git a/addons/sale/static/src/js/product_configurator_mixin.js b/addons/sale/static/src/js/product_configurator_mixin.js index 80471814207..7e7847215a7 100644 --- a/addons/sale/static/src/js/product_configurator_mixin.js +++ b/addons/sale/static/src/js/product_configurator_mixin.js @@ -68,7 +68,7 @@ var ProductConfiguratorMixin = { self._checkExclusions($parent, combination); - ajax.jsonRpc(this._getUri('/product_configurator/get_combination_info'), 'call', { + ajax.jsonRpc(this._getUri('/sale/get_combination_info'), 'call', { product_template_id: parseInt($parent.find('.product_template_id').val()), product_id: this._getProductId($parent), combination: combination, @@ -311,7 +311,7 @@ var ProductConfiguratorMixin = { // We don't handle that compatibility because the previous code was // not working either: it was making an RPC that failed with any // non-admin user anyway. To use this feature, restart the server. - var route = '/product_configurator/create_product_variant'; + var route = '/sale/create_product_variant'; if (useAjax) { productReady = ajax.jsonRpc(route, 'call', params); } else { diff --git a/addons/sale/views/assets.xml b/addons/sale/views/assets.xml index ea5c82b1c86..9e7e42eff80 100644 --- a/addons/sale/views/assets.xml +++ b/addons/sale/views/assets.xml @@ -8,11 +8,6 @@ - - - - - @@ -28,7 +23,6 @@ diff --git a/addons/sale/views/res_config_settings_views.xml b/addons/sale/views/res_config_settings_views.xml index bc1366e5d5c..3f2bb403453 100644 --- a/addons/sale/views/res_config_settings_views.xml +++ b/addons/sale/views/res_config_settings_views.xml @@ -18,7 +18,7 @@
+
+
+ +
+
+
+
diff --git a/addons/sale/views/sale_views.xml b/addons/sale/views/sale_views.xml index bcf728e356b..e793650ecb6 100644 --- a/addons/sale/views/sale_views.xml +++ b/addons/sale/views/sale_views.xml @@ -81,13 +81,6 @@ 0 - - - - - - - diff --git a/addons/sale/views/variant_templates.xml b/addons/sale/views/variant_templates.xml new file mode 100644 index 00000000000..8d52d860be4 --- /dev/null +++ b/addons/sale/views/variant_templates.xml @@ -0,0 +1,108 @@ + + + + diff --git a/addons/sale/wizard/__init__.py b/addons/sale/wizard/__init__.py index 145cce89c1e..09f069f85ed 100644 --- a/addons/sale/wizard/__init__.py +++ b/addons/sale/wizard/__init__.py @@ -3,4 +3,3 @@ from . import payment_acquirer_onboarding_wizard from . import sale_make_invoice_advance -from . import sale_product_configurator \ No newline at end of file diff --git a/addons/sale_management/__manifest__.py b/addons/sale_management/__manifest__.py index 3bf801a604b..98f77d20956 100644 --- a/addons/sale_management/__manifest__.py +++ b/addons/sale_management/__manifest__.py @@ -47,7 +47,6 @@ The Dashboard for the Sales Manager will include 'views/sale_management_views.xml', 'views/digest_views.xml', 'views/sale_order_views.xml', - 'views/assets.xml', 'report/sale_report_templates.xml', ], 'demo': [ diff --git a/addons/sale_management/tests/test_sale_ui.py b/addons/sale_management/tests/test_sale_ui.py index 6ec2f4167c1..cf107b02ebc 100644 --- a/addons/sale_management/tests/test_sale_ui.py +++ b/addons/sale_management/tests/test_sale_ui.py @@ -7,119 +7,3 @@ class TestUi(odoo.tests.HttpCase): def test_01_sale_tour(self): self.phantom_js("/web", "odoo.__DEBUG__.services['web_tour.tour'].run('sale_tour')", "odoo.__DEBUG__.services['web_tour.tour'].tours.sale_tour.ready", login="admin") - - def test_02_product_configurator(self): - # group_product_variant: use the product configurator - # group_sale_pricelist: display the pricelist to determine when it is changed after choosing - # the partner - self.env.ref('base.user_admin').write({ - 'groups_id': [ - (4, self.env.ref('product.group_product_variant').id), - (4, self.env.ref('product.group_sale_pricelist').id), - ], - }) - - self.phantom_js("/web", "odoo.__DEBUG__.services['web_tour.tour'].run('sale_product_configurator_tour')", "odoo.__DEBUG__.services['web_tour.tour'].tours.sale_product_configurator_tour.ready", login="admin") - - def test_03_product_configurator_advanced(self): - # group_product_variant: use the product configurator - # group_sale_pricelist: display the pricelist to determine when it is changed after choosing - # the partner - self.env.ref('base.user_admin').write({ - 'groups_id': [ - (4, self.env.ref('product.group_product_variant').id), - (4, self.env.ref('product.group_sale_pricelist').id), - ], - }) - - # Prepare relevant test data - # This is not included in demo data to avoid useless noise - product_attributes = self.env['product.attribute'].create([{ - 'name': 'PA1', - 'type': 'radio', - 'create_variant': 'dynamic' - }, { - 'name': 'PA2', - 'type': 'radio', - 'create_variant': 'always' - }, { - 'name': 'PA3', - 'type': 'radio', - 'create_variant': 'dynamic' - }, { - 'name': 'PA4', - 'type': 'select', - 'create_variant': 'no_variant' - }, { - 'name': 'PA5', - 'type': 'select', - 'create_variant': 'no_variant' - }, { - 'name': 'PA7', - 'type': 'color', - 'create_variant': 'no_variant' - }, { - 'name': 'PA8', - 'type': 'radio', - 'create_variant': 'no_variant' - }]) - - product_attribute_values = self.env['product.attribute.value'].create([{ - 'name': 'PAV' + str(i), - 'is_custom': i == 9, - 'attribute_id': product_attribute.id - } for i in range(1, 11) for product_attribute in product_attributes]) - - product_template_attribute_lines = self.env['product.template.attribute.line'].create([{ - 'attribute_id': product_attribute.id, - 'product_tmpl_id': self.env.ref("product.product_product_4").id, - 'value_ids': [(6, 0, product_attribute_values.filtered( - lambda product_attribute_value: product_attribute_value.attribute_id == product_attribute - ).ids)] - } for product_attribute in product_attributes]) - - self.env.ref("product.product_product_4").update({ - 'attribute_line_ids': [(4, product_template_attribute_line.id) for product_template_attribute_line in product_template_attribute_lines] - }) - - self.phantom_js("/web", "odoo.__DEBUG__.services['web_tour.tour'].run('sale_product_configurator_advanced_tour')", "odoo.__DEBUG__.services['web_tour.tour'].tours.sale_product_configurator_advanced_tour.ready", login="admin") - - def test_04_product_configurator_pricelist(self): - """The goal of this test is to make sure pricelist rules are correctly - applied on the backend product configurator. - Also testing B2C setting: no impact on the backend configurator. - """ - - admin = self.env.ref('base.user_admin') - - # Activate B2C - self.env.ref('account.group_show_line_subtotals_tax_included').users |= admin - self.env.ref('account.group_show_line_subtotals_tax_excluded').users -= admin - - # Active pricelist on SO - self.env.ref('product.group_sale_pricelist').users |= admin - - # Add a 15% tax on desk - tax = self.env['account.tax'].create({'name': "Test tax", 'amount': 15}) - self.env.ref('product.product_product_4_product_template').taxes_id = tax - - # Remove tax from Conference Chair and Chair floor protection - self.env.ref('sale.product_product_1_product_template').taxes_id = None - self.env.ref('product.product_product_11_product_template').taxes_id = None - - # Make sure pricelist rule exist - product_template = self.env.ref('product.product_product_4_product_template') - pricelist = self.env.ref('product.list0') - - if not pricelist.item_ids.filtered(lambda i: i.product_tmpl_id == product_template and i.price_discount == 20): - self.env['product.pricelist.item'].create({ - 'base': 'list_price', - 'applied_on': '1_product', - 'pricelist_id': pricelist.id, - 'product_tmpl_id': product_template.id, - 'price_discount': 20, - 'min_quantity': 2, - 'compute_price': 'formula', - }) - - self.phantom_js("/web", "odoo.__DEBUG__.services['web_tour.tour'].run('sale_product_configurator_pricelist_tour')", "odoo.__DEBUG__.services['web_tour.tour'].tours.sale_product_configurator_pricelist_tour.ready", login="admin") diff --git a/addons/sale_management/views/assets.xml b/addons/sale_management/views/assets.xml deleted file mode 100644 index a5a3134949d..00000000000 --- a/addons/sale_management/views/assets.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - + diff --git a/addons/sale_product_configurator/views/sale_views.xml b/addons/sale_product_configurator/views/sale_views.xml new file mode 100644 index 00000000000..499cb2c7eb0 --- /dev/null +++ b/addons/sale_product_configurator/views/sale_views.xml @@ -0,0 +1,17 @@ + + + + product.template.form.inherit.sale.product.configurator + product.template + + + + + + + + + + + + diff --git a/addons/sale/views/sale_product_configurator_templates.xml b/addons/sale_product_configurator/views/templates.xml similarity index 59% rename from addons/sale/views/sale_product_configurator_templates.xml rename to addons/sale_product_configurator/views/templates.xml index 1f0ec29d534..534a199dc70 100644 --- a/addons/sale/views/sale_product_configurator_templates.xml +++ b/addons/sale_product_configurator/views/templates.xml @@ -2,12 +2,12 @@ -