[FIX] product: a temporal variant copy for future convenience
Reproduction: 1. Create a dynamic attribute "dyn_att" with a couple of values 2. Create a product template "dyn_prod" with those attribute values 3. Create an order for "dyn_prod", this will trigger creating a variant 4. Make sure to check Variant Grid Entry in Sales Settings 5. Open variant form view of dyn_prod and edit it to allow duplication 6. Duplicating it leads to an error Reason: copying the variant is not possible and disabled here: https://github.com/odoo/odoo/pull/38303 For future convenience, maybe it’s better to give a temporal working solution. The function _create_first_product_variant is used in the product module but only defined in its child module website_sale Fix: copy the product template, create and return its first possible variant. Added test for dynamic variant copy. change the definition place of _create_first_product_variant to module product opw-2790543 closes odoo/odoo#98809 X-original-commit: 34a2948d3d6e0f597c1b5d65f9f118e891c07cc4 Signed-off-by: Nicolas Lempereur (nle) <nle@odoo.com> Signed-off-by: Liu Jinjiu (jili) <jili@odoo.com>
This commit is contained in:
@@ -386,7 +386,12 @@ class ProductProduct(models.Model):
|
||||
For convenience the template is copied instead and its first variant is
|
||||
returned.
|
||||
"""
|
||||
return self.product_tmpl_id.copy(default=default).product_variant_id
|
||||
# copy variant is disabled in https://github.com/odoo/odoo/pull/38303
|
||||
# this returns the first possible combination of variant to make it
|
||||
# works for now, need to be fixed to return product_variant_id if it's
|
||||
# possible in the future
|
||||
template = self.product_tmpl_id.copy(default=default)
|
||||
return template.product_variant_id or template._create_first_product_variant()
|
||||
|
||||
@api.model
|
||||
def _search(self, args, offset=0, limit=None, order=None, count=False, access_rights_uid=None):
|
||||
|
||||
@@ -1006,6 +1006,18 @@ class ProductTemplate(models.Model):
|
||||
'product_template_attribute_value_ids': [(6, 0, combination._without_no_variant_attributes().ids)]
|
||||
})
|
||||
|
||||
def _create_first_product_variant(self, log_warning=False):
|
||||
"""Create if necessary and possible and return the first product
|
||||
variant for this template.
|
||||
|
||||
:param log_warning: whether a warning should be logged on fail
|
||||
:type log_warning: bool
|
||||
|
||||
:return: the first product variant or none
|
||||
:rtype: recordset of `product.product`
|
||||
"""
|
||||
return self._create_product_variant(self._get_first_possible_combination(), log_warning)
|
||||
|
||||
@tools.ormcache('self.id', 'frozenset(filtered_combination.ids)')
|
||||
def _get_variant_id_for_combination(self, filtered_combination):
|
||||
"""See `_get_variant_for_combination`. This method returns an ID
|
||||
|
||||
@@ -211,6 +211,31 @@ class TestVariants(ProductVariantsCommon):
|
||||
self.assertEqual(variant_copy.name, 'Test Copy (copy) (copy)')
|
||||
self.assertEqual(len(variant_copy.product_variant_ids), 2)
|
||||
|
||||
def test_dynamic_variants_copy(self):
|
||||
self.color_attr = self.env['product.attribute'].create({'name': 'Color', 'create_variant': 'dynamic'})
|
||||
self.color_attr_value_r = self.env['product.attribute.value'].create({'name': 'Red', 'attribute_id': self.color_attr.id})
|
||||
self.color_attr_value_b = self.env['product.attribute.value'].create({'name': 'Blue', 'attribute_id': self.color_attr.id})
|
||||
|
||||
# test copy of variant with dynamic attribute
|
||||
template_dyn = self.env['product.template'].create({
|
||||
'name': 'Test Dynamical',
|
||||
'attribute_line_ids': [(0, 0, {
|
||||
'attribute_id': self.color_attr.id,
|
||||
'value_ids': [(4, self.color_attr_value_r.id), (4, self.color_attr_value_b.id)],
|
||||
})]
|
||||
})
|
||||
|
||||
self.assertEqual(len(template_dyn.product_variant_ids), 0)
|
||||
self.assertEqual(template_dyn.name, 'Test Dynamical')
|
||||
|
||||
variant_dyn = template_dyn._create_product_variant(template_dyn._get_first_possible_combination())
|
||||
self.assertEqual(len(template_dyn.product_variant_ids), 1)
|
||||
|
||||
variant_dyn_copy = variant_dyn.copy()
|
||||
template_dyn_copy = variant_dyn_copy.product_tmpl_id
|
||||
self.assertEqual(len(template_dyn_copy.product_variant_ids), 1)
|
||||
self.assertEqual(template_dyn_copy.name, 'Test Dynamical (copy)')
|
||||
|
||||
def test_standard_price(self):
|
||||
""" Ensure template values are correctly (re)computed depending on the context """
|
||||
one_variant_product = self.product
|
||||
|
||||
@@ -282,18 +282,6 @@ class ProductTemplate(models.Model):
|
||||
# The list_price is always the price of one.
|
||||
return taxes.compute_all(price, pricelist.currency_id, 1, product, partner)[tax_display]
|
||||
|
||||
def _create_first_product_variant(self, log_warning=False):
|
||||
"""Create if necessary and possible and return the first product
|
||||
variant for this template.
|
||||
|
||||
:param log_warning: whether a warning should be logged on fail
|
||||
:type log_warning: bool
|
||||
|
||||
:return: the first product variant or none
|
||||
:rtype: recordset of `product.product`
|
||||
"""
|
||||
return self._create_product_variant(self._get_first_possible_combination(), log_warning)
|
||||
|
||||
def _get_image_holder(self):
|
||||
"""Returns the holder of the image to use as default representation.
|
||||
If the product template has an image it is the product template,
|
||||
|
||||
Reference in New Issue
Block a user