diff --git a/addons/mrp/models/mrp_bom.py b/addons/mrp/models/mrp_bom.py index c8b965a89c3..58df0ad6d14 100644 --- a/addons/mrp/models/mrp_bom.py +++ b/addons/mrp/models/mrp_bom.py @@ -6,6 +6,8 @@ from odoo.addons import decimal_precision as dp from odoo.exceptions import UserError, ValidationError from odoo.tools import float_round +from itertools import groupby + class MrpBom(models.Model): """ Defines bills of material for a product or a product template """ @@ -292,8 +294,10 @@ class MrpBomLine(models.Model): custom control. It currently checks that all variant values are in the product. """ if self.attribute_value_ids: - if not product or self.attribute_value_ids - product.attribute_value_ids: - return True + for att, att_values in groupby(self.attribute_value_ids, lambda l: l.attribute_id): + values = self.env['product.attribute.value'].concat(*list(att_values)) + if not (product.attribute_value_ids & values): + return True return False @api.multi diff --git a/addons/mrp/tests/test_bom.py b/addons/mrp/tests/test_bom.py index 0cd8f9effa4..44fd56b6431 100644 --- a/addons/mrp/tests/test_bom.py +++ b/addons/mrp/tests/test_bom.py @@ -161,3 +161,70 @@ class TestBoM(TestMrpCommon): }) with self.assertRaises(exceptions.UserError): test_bom_3.explode(self.product_9, 1) + + def test_multi_level_variants2(self): + """Test skip bom line with same attribute values in bom lines.""" + + Product = self.env['product.product'] + ProductAttribute = self.env['product.attribute'] + ProductAttributeValue = self.env['product.attribute.value'] + MrpProduction = self.env['mrp.production'] + + # Product Attribute + att_color = ProductAttribute.create({'name': 'Color'}) + att_size = ProductAttribute.create({'name': 'size'}) + + # Product Attribute color Value + att_color_red = ProductAttributeValue.create({'name': 'red', 'attribute_id': att_color.id}) + att_color_blue = ProductAttributeValue.create({'name': 'blue', 'attribute_id': att_color.id}) + # Product Attribute size Value + att_size_big = ProductAttributeValue.create({'name': 'big', 'attribute_id': att_size.id}) + att_size_medium = ProductAttributeValue.create({'name': 'medium', 'attribute_id': att_size.id}) + + # Create Template Product + product_template = self.env['product.template'].create({ + 'name': 'Sofa', + 'attribute_line_ids': [(0, 0, { + 'attribute_id': att_color.id, + 'value_ids': [(6, 0, [att_color_red.id, att_color_blue.id])] + }), (0, 0, { + 'attribute_id': att_size.id, + 'value_ids': [(6, 0, [att_size_big.id, att_size_medium.id])] + })] + }) + + # Create components Of BOM + product_A = Product.create({ + 'name': 'Wood'}) + product_B = Product.create({ + 'name': 'Clothes'}) + + # Create BOM + test_bom = self.env['mrp.bom'].create({ + 'product_tmpl_id': product_template.id, + 'product_qty': 1.0, + 'type': 'normal', + 'bom_line_ids': [(0, 0, { + 'product_id': product_A.id, + 'product_qty': 1, + 'attribute_value_ids': [(4, att_color_red.id), (4, att_color_blue.id), (4, att_size_big.id)], + }), (0, 0, { + 'product_id': product_B.id, + 'product_qty': 1, + 'attribute_value_ids': [(4, att_color_red.id), (4, att_color_blue.id)] + })] + }) + + combination = {(att_color_red, att_size_big): [product_A.id, product_B.id] , (att_color_red, att_size_medium): [product_B.id] , (att_color_blue, att_size_big):[product_A.id, product_B.id], (att_color_blue, att_size_medium): [product_B.id]} + + # Create production order for all variants. + for comb in combination.keys(): + consu_product_ids = combination[comb] + product = product_template.product_variant_ids.filtered(lambda x: all(value in comb for value in x.attribute_value_ids)) + mrp_order = MrpProduction.create({ + 'product_uom_id': product.uom_id.id, + 'product_id': product.id, + 'bom_id': test_bom.id, + }) + # Check consumed materials in production order. + self.assertEqual(mrp_order.move_raw_ids.mapped('product_id').ids, consu_product_ids)