From aacd4bca5ad207cb145611caa61da9114469ac7c Mon Sep 17 00:00:00 2001 From: "Manu Vaillant (manv)" Date: Wed, 28 Feb 2024 16:29:15 +0100 Subject: [PATCH] [FIX] point_of_sale: fix combo choice reference on variants creation -When product in combo choices are being archived because of variants creation, replace the archived reference with all newly created variants. -Add test to reproduce this use case task id: 3713861 closes odoo/odoo#156301 Signed-off-by: Vlad Stroia (vlst) --- addons/point_of_sale/models/product.py | 24 +++++++++++ .../point_of_sale/tests/test_point_of_sale.py | 40 +++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/addons/point_of_sale/models/product.py b/addons/point_of_sale/models/product.py index 99287159a5b..82af00add11 100644 --- a/addons/point_of_sale/models/product.py +++ b/addons/point_of_sale/models/product.py @@ -45,6 +45,30 @@ class ProductTemplate(models.Model): if combo_name: raise UserError(_('You must first remove this product from the %s combo', combo_name)) + def _create_variant_ids(self): + res = super()._create_variant_ids() + for template in self: + archived_product = self.env['product.product'].search([('product_tmpl_id', '=', template.id), ('active', '=', False)], limit=1) + if archived_product: + combo_choices_to_delete = self.env['pos.combo.line'].search([ + ('product_id', '=', archived_product.id) + ]) + if combo_choices_to_delete: + # Delete old combo line + combo_ids = combo_choices_to_delete.mapped('combo_id') + combo_choices_to_delete.unlink() + # Create new combo line (one for each new variant) in each combo + new_variants = template.product_variant_ids.filtered(lambda v: v.active) + self.env['pos.combo.line'].create([ + { + 'product_id': variant.id, + 'combo_id': combo_id.id, + } + for variant in new_variants for combo_id in combo_ids + ]) + return res + + class ProductProduct(models.Model): _inherit = 'product.product' diff --git a/addons/point_of_sale/tests/test_point_of_sale.py b/addons/point_of_sale/tests/test_point_of_sale.py index 478f08357b7..1c76ea825a3 100644 --- a/addons/point_of_sale/tests/test_point_of_sale.py +++ b/addons/point_of_sale/tests/test_point_of_sale.py @@ -45,3 +45,43 @@ class TestPointOfSale(TransactionCase): self.assertEqual(new_config.pricelist_id, self.env['product.pricelist'], "POS config incorrectly has pricelist %s" % new_config.pricelist_id.display_name) + + def test_product_combo_variants(self): + # Create product and combo + product = self.env['product.product'].create({ + 'name': 'Test Product 1', + 'list_price': 100, + 'taxes_id': False, + 'available_in_pos': True, + }) + + product_1_combo_line = self.env["pos.combo.line"].create({ + "product_id": product.id, + }) + + pos_combo = self.env["pos.combo"].create( + { + "name": "Pos combo", + "combo_line_ids": [ + (6, 0, [product_1_combo_line.id]) + ], + } + ) + # Add attribute and values, simulating variant creation + size_attribute = self.env['product.attribute'].create({'name': 'Size'}) + attribute_value_1 = self.env['product.attribute.value'].create({'name': 'Large', 'attribute_id': size_attribute.id}) + attribute_value_2 = self.env['product.attribute.value'].create({'name': 'Small', 'attribute_id': size_attribute.id}) + original_product_id = product.id + product_template = product.product_tmpl_id + product.product_tmpl_id.with_context(create_product_product=True).write({ + 'attribute_line_ids': [(0, 0, { + 'attribute_id': size_attribute.id, + 'value_ids': [(6, 0, [attribute_value_1.id, attribute_value_2.id])], + })], + }) + # Check that original product should not be in combo anymore (replace by variants) + self.assertTrue(original_product_id not in pos_combo.combo_line_ids.mapped('product_id').ids, "Original product should not be in combo") + # Check that variants are in combo + variant_ids = product_template.product_variant_ids.ids + for variant_id in variant_ids: + self.assertIn(variant_id, pos_combo.combo_line_ids.mapped('product_id').ids, "Variant should be in combo")