This commit has several parts: - fix ordering of attributes and values - fix/create python logic (notably for checking the possibility of a combination and for price computing) - fix currency - display correct info on the views without the need of JS (prevent flickering) - fix various issues with website and SO - add a lot of tests (both unit and tour) ================================================================================ *** Make consistent the filtering and ordering of product variants *** Filter & Check possibility ========================== The first variant shown on the website should always be possible. When activating the customize "list view of variants", we don't want to show impossible combination on the list. We also don't want the user to be able to add to cart an impossible variant. Show appropriate messages when no combination is possible. Order ===== We want the variant order to be consistent with what the user selected with the sequence fields. The display order on the website should be the same than the display on the various backend views. This implies also fixing the order of: - product.attribute - product.attribute.value - product.template.attribute.line - product.template.attribute.value The general order is now: (parent,) sequence, id. Before, it was already a mix of those, but not always in the same order, and not every key for every model. The name has been removed from the order: either we sequence them correctly, or we want them in the creation order. On the website, the first combination shown by the python should be the same as the one that will be computed by the JS to prevent flickering. Ideally there should be no RPC at page loading if the python has computed everything correctly, but it is currently not possible to achieve because the view logic for website_sale_stock is only in JS. ================================================================================ *** Fix product template and variant price computation *** Move and improve the get_combination_info method: - take into account tax included/excluded settings (B2B/B2C) - take into account pricelist discount policy (striked price) - fix inconsistencies when price after pricelist would be 0 - use proper rounding and discount compute with the currency helper methods - fix an issue where context "partner" would have a mix of id and recordset ================================================================================ *** Fix currency conversions *** The list_price and lst_price of a product are always encoded using the currency of the product. The results of "combination info" are always returned using the currency of the pricelist if given, or the currency of the product. The prices on an order (line) are encoded with the currency of its pricelist. ================================================================================ *** Fix dynamic variants access rights *** Public users on the e-commerce should be able to create product variants if the configuration of the attributes of the product template is set as "dynamic". This uses "sudo" to bypass create security rules for public users. Multiple checks are done to ensure that it creates a valid product variant. ================================================================================ *** Miscellaneous bugs, tracebacks and necessary improvements *** In general: - correctly show warning when no variant is possible - don't let user add to cart an impossible variant (from optional product modal or from any RPC), both in interface and controller logic - fix as much flickering as possible by correctly pre-computing in python/views - move business logic from controllers to models - correctly add no_variant and is_custom values on the SO line description Sale order: - fix traceback when emptying the product_id field from configurator website_sale: - correctly compute first image / carousel, and correctly swap images when changing variants, without duplicate image or zoom problems - don't use list view of variants when it doesn't make sense - added missing product info when adding a no_variant attribute to cart - fix cart accessory product computation website_sale_wishlist: - fix it with dynamic variants, no_variant and is_custom website_sale_comparison: - fix it with dynamic variants (comparison and alternative products) website_sale_stock: - fix it in general (some flickering still present) ================================================================================ Authored by @seb-odoo with contribution from @awa-odoo task-1910821 closes odoo/odoo#28079
208 lines
8.5 KiB
Python
208 lines
8.5 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
import odoo.tests
|
|
|
|
|
|
@odoo.tests.common.tagged('post_install', '-at_install')
|
|
class TestUi(odoo.tests.HttpCase):
|
|
|
|
def test_01_admin_shop_customize_tour(self):
|
|
# create a template
|
|
product_template = self.env['product.template'].create({
|
|
'name': 'Test Product',
|
|
'website_published': True,
|
|
'list_price': 750,
|
|
})
|
|
product_attribute = self.env.ref('product.product_attribute_1')
|
|
product_attribute_value_1 = self.env.ref('product.product_attribute_value_1')
|
|
product_attribute_value_2 = self.env.ref('product.product_attribute_value_2')
|
|
|
|
# set attribute and attribute values on the template
|
|
self.env['product.template.attribute.line'].create([{
|
|
'attribute_id': product_attribute.id,
|
|
'product_tmpl_id': product_template.id,
|
|
'value_ids': [(6, 0, [product_attribute_value_1.id, product_attribute_value_2.id])]
|
|
}])
|
|
|
|
# set a different price on the variants to differentiate them
|
|
product_template_attribute_values = self.env['product.template.attribute.value'] \
|
|
.search([('product_tmpl_id', '=', product_template.id)])
|
|
|
|
for ptav in product_template_attribute_values:
|
|
if ptav.name == "Steel":
|
|
ptav.price_extra = 0
|
|
else:
|
|
ptav.price_extra = 50.4
|
|
|
|
product_template.create_variant_ids()
|
|
|
|
self.phantom_js("/", "odoo.__DEBUG__.services['web_tour.tour'].run('shop_customize')", "odoo.__DEBUG__.services['web_tour.tour'].tours.shop_customize.ready", login="admin")
|
|
|
|
def test_02_admin_shop_custom_attribute_value_tour(self):
|
|
# Make sure pricelist rule exist
|
|
product_template = self.env.ref('product.product_product_4_product_template')
|
|
|
|
# fix runbot, sometimes one pricelist is chosen, sometimes the other...
|
|
pricelists = self.env['website'].get_current_website().get_current_pricelist() | self.env.ref('product.list0')
|
|
|
|
for pricelist in pricelists:
|
|
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',
|
|
})
|
|
|
|
pricelist.discount_policy = 'without_discount'
|
|
|
|
self.phantom_js("/", "odoo.__DEBUG__.services['web_tour.tour'].run('shop_custom_attribute_value')", "odoo.__DEBUG__.services['web_tour.tour'].tours.shop_custom_attribute_value.ready", login="admin")
|
|
|
|
def test_03_public_tour_shop_dynamic_variants(self):
|
|
""" The goal of this test is to make sure product variants with dynamic
|
|
attributes can be created by the public user (when being added to cart).
|
|
"""
|
|
|
|
# create the attribute
|
|
product_attribute = self.env['product.attribute'].create({
|
|
'name': "Dynamic Attribute",
|
|
'create_variant': 'dynamic',
|
|
})
|
|
|
|
# create the attribute values
|
|
product_attribute_values = self.env['product.attribute.value'].create([{
|
|
'name': "Dynamic Value 1",
|
|
'attribute_id': product_attribute.id,
|
|
'sequence': 1,
|
|
}, {
|
|
'name': "Dynamic Value 2",
|
|
'attribute_id': product_attribute.id,
|
|
'sequence': 2,
|
|
}])
|
|
|
|
# create the template
|
|
product_template = self.env['product.template'].create({
|
|
'name': 'Dynamic Product',
|
|
'website_published': True,
|
|
'list_price': 0,
|
|
})
|
|
|
|
# set attribute and attribute values on the template
|
|
self.env['product.template.attribute.line'].create([{
|
|
'attribute_id': product_attribute.id,
|
|
'product_tmpl_id': product_template.id,
|
|
'value_ids': [(6, 0, product_attribute_values.ids)]
|
|
}])
|
|
|
|
# set a different price on the variants to differentiate them
|
|
product_template_attribute_values = self.env['product.template.attribute.value'] \
|
|
.search([('product_tmpl_id', '=', product_template.id)])
|
|
|
|
for ptav in product_template_attribute_values:
|
|
if ptav.name == "Dynamic Value 1":
|
|
ptav.price_extra = 10
|
|
else:
|
|
# 0 to not bother with the pricelist of the public user
|
|
ptav.price_extra = 0
|
|
|
|
self.phantom_js("/", "odoo.__DEBUG__.services['web_tour.tour'].run('tour_shop_dynamic_variants')", "odoo.__DEBUG__.services['web_tour.tour'].tours.tour_shop_dynamic_variants.ready")
|
|
|
|
def test_04_portal_tour_deleted_archived_variants(self):
|
|
"""The goal of this test is to make sure deleted and archived variants
|
|
are shown as impossible combinations.
|
|
|
|
Using "portal" to have various users in the tests.
|
|
"""
|
|
|
|
# create the attribute
|
|
product_attribute = self.env['product.attribute'].create({
|
|
'name': "My Attribute",
|
|
'create_variant': 'always',
|
|
})
|
|
|
|
# create the attribute values
|
|
product_attribute_values = self.env['product.attribute.value'].create([{
|
|
'name': "My Value 1",
|
|
'attribute_id': product_attribute.id,
|
|
'sequence': 1,
|
|
}, {
|
|
'name': "My Value 2",
|
|
'attribute_id': product_attribute.id,
|
|
'sequence': 2,
|
|
}, {
|
|
'name': "My Value 3",
|
|
'attribute_id': product_attribute.id,
|
|
'sequence': 3,
|
|
}])
|
|
|
|
# create the template
|
|
product_template = self.env['product.template'].create({
|
|
'name': 'Test Product',
|
|
'website_published': True,
|
|
})
|
|
|
|
# set attribute and attribute values on the template
|
|
self.env['product.template.attribute.line'].create([{
|
|
'attribute_id': product_attribute.id,
|
|
'product_tmpl_id': product_template.id,
|
|
'value_ids': [(6, 0, product_attribute_values.ids)]
|
|
}])
|
|
|
|
# set a different price on the variants to differentiate them
|
|
product_template_attribute_values = self.env['product.template.attribute.value'] \
|
|
.search([('product_tmpl_id', '=', product_template.id)])
|
|
|
|
product_template_attribute_values[0].price_extra = 10
|
|
product_template_attribute_values[1].price_extra = 20
|
|
product_template_attribute_values[2].price_extra = 30
|
|
|
|
product_template.create_variant_ids()
|
|
|
|
# archive first and delete second
|
|
product_template.product_variant_ids[0].active = False
|
|
product_template.product_variant_ids[1].unlink()
|
|
|
|
self.phantom_js("/", "odoo.__DEBUG__.services['web_tour.tour'].run('tour_shop_deleted_archived_variants')", "odoo.__DEBUG__.services['web_tour.tour'].tours.tour_shop_deleted_archived_variants.ready", login="portal")
|
|
|
|
def test_05_demo_tour_no_variant_attribute(self):
|
|
"""The goal of this test is to make sure attributes no_variant are
|
|
correctly added to cart.
|
|
|
|
Using "demo" to have various users in the tests.
|
|
"""
|
|
|
|
# create the attribute
|
|
product_attribute_no_variant = self.env['product.attribute'].create({
|
|
'name': "No Variant Attribute",
|
|
'create_variant': 'no_variant',
|
|
})
|
|
|
|
# create the attribute value
|
|
product_attribute_value_no_variant = self.env['product.attribute.value'].create({
|
|
'name': "No Variant Value",
|
|
'attribute_id': product_attribute_no_variant.id,
|
|
})
|
|
|
|
# create the template
|
|
product_template = self.env['product.template'].create({
|
|
'name': 'Test Product',
|
|
'website_published': True,
|
|
})
|
|
|
|
# set attribute and attribute value on the template
|
|
ptal = self.env['product.template.attribute.line'].create([{
|
|
'attribute_id': product_attribute_no_variant.id,
|
|
'product_tmpl_id': product_template.id,
|
|
'value_ids': [(6, 0, product_attribute_value_no_variant.ids)]
|
|
}])
|
|
|
|
# set a price on the value
|
|
ptal.product_template_value_ids.price_extra = 10
|
|
|
|
product_template.create_variant_ids()
|
|
|
|
self.phantom_js("/", "odoo.__DEBUG__.services['web_tour.tour'].run('tour_shop_no_variant_attribute')", "odoo.__DEBUG__.services['web_tour.tour'].tours.tour_shop_no_variant_attribute.ready", login="demo")
|