From 38a3f7fea47006567880edf965c3ec29eae4eaed Mon Sep 17 00:00:00 2001 From: Adrien Widart Date: Tue, 28 Dec 2021 16:33:46 +0000 Subject: [PATCH] [FIX] {purchase_}stock, product: base product name on supplier Suppose a product with several suppliers, all with the same partner. On the purchase order, the product description will always be based on the last supplier To reproduce the issue: 1. Create a vendor V 2. Create a product P: - Type: Storable - In Purchase, add a line L01: - Vendor: V - Vendor Product Name: Name01 - Vendor Product Code: C01 - Quantity: 1 - Price: 10 - In Purchase, add a second line L02: - Vendor: V - Vendor Product Name: Name02 - Vendor Product Code: C02 - Quantity: 20 - Price: 2 - Once P is saved, ensure the lines order in the purchase tab: - L01 - L02 3. Add a reordering rule on P: - Min: 1 4. Run the scheduler 5. Open the generated PO Error: The description is incorrect ("[C02] Name02" instead of "[C01] Name01") When computing the display name of the product, https://github.com/odoo/odoo/blob/7691567286869ca65e63fc79c2cee11e1f415fcb/odoo/models.py#L1728-L1730 `name_get` returns a tuples list: `[(37, '[C01] Name01'), (37, '[C02] Name02')]` where `37` is the product identifier. This list is then converted into a dictionary and here is the issue: it will use the last tuple to define the value for key `37`, i.e. "[C02] Name02". Therefore, `name_get` should return the correct name, and only this one. Another issue could be highlighted: when the user changes the quantity of the purchase order line, if another supplier info is selected, the description won't be updated (for the same reason as above) OPW-2702616 closes odoo/odoo#82321 X-original-commit: a42608214f2e9ef3f5e59b4b54cd7f72a6019e06 Signed-off-by: Tiffany Chang Signed-off-by: Adrien Widart --- addons/product/models/product.py | 8 ++-- addons/purchase/models/purchase.py | 4 +- .../tests/test_purchase_order.py | 42 +++++++++++++++++++ 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/addons/product/models/product.py b/addons/product/models/product.py index 2e832ed728b..32f652316e9 100644 --- a/addons/product/models/product.py +++ b/addons/product/models/product.py @@ -427,10 +427,10 @@ class ProductProduct(models.Model): args.append((('categ_id', 'child_of', self._context['search_default_categ_id']))) return super(ProductProduct, self)._search(args, offset=offset, limit=limit, order=order, count=count, access_rights_uid=access_rights_uid) - @api.depends_context('display_default_code') + @api.depends_context('display_default_code', 'seller_id') def _compute_display_name(self): # `display_name` is calling `name_get()`` which is overidden on product - # to depend on `display_default_code` + # to depend on `display_default_code` and `seller_id` return super()._compute_display_name() def name_get(self): @@ -478,8 +478,8 @@ class ProductProduct(models.Model): variant = product.product_template_attribute_value_ids._get_combination_name() name = variant and "%s (%s)" % (product.name, variant) or product.name - sellers = [] - if partner_ids: + sellers = self.env['product.supplierinfo'].sudo().browse(self.env.context.get('seller_id')) or [] + if not sellers and partner_ids: product_supplier_info = supplier_info_by_template.get(product.product_tmpl_id, []) sellers = [x for x in product_supplier_info if x.product_id and x.product_id == product] if not sellers: diff --git a/addons/purchase/models/purchase.py b/addons/purchase/models/purchase.py index b8c0e80a31d..90200712f6f 100644 --- a/addons/purchase/models/purchase.py +++ b/addons/purchase/models/purchase.py @@ -1194,6 +1194,8 @@ class PurchaseOrderLine(models.Model): price_unit = seller.product_uom._compute_price(price_unit, self.product_uom) self.price_unit = price_unit + product_ctx = {'seller_id': seller.id, 'lang': get_lang(self.env, self.partner_id.lang).code} + self.name = self._get_product_purchase_description(self.product_id.with_context(product_ctx)) @api.onchange('product_id', 'product_qty', 'product_uom') def _onchange_suggest_packaging(self): @@ -1353,7 +1355,7 @@ class PurchaseOrderLine(models.Model): lang=partner.lang, partner_id=partner.id, ) - name = product_lang.display_name + name = product_lang.with_context(seller_id=seller.id).display_name if product_lang.description_purchase: name += '\n' + product_lang.description_purchase diff --git a/addons/purchase_stock/tests/test_purchase_order.py b/addons/purchase_stock/tests/test_purchase_order.py index bebeb1083ee..7c2314cbb15 100644 --- a/addons/purchase_stock/tests/test_purchase_order.py +++ b/addons/purchase_stock/tests/test_purchase_order.py @@ -410,3 +410,45 @@ class TestPurchaseOrder(ValuationReconciliationTestCommon): _message_content = _purchase_order.message_ids.mapped("body")[0] self.assertIsNotNone(re.search(r"Received Quantity: 5.0 -> 10.0", _message_content), "Already received quantity isn't correctly taken into consideration") + + def test_pol_description(self): + """ + Suppose a product with several sellers, all with the same partner. On the purchase order, the product + description should be based on the correct seller + """ + self.env.user.write({'company_id': self.company_data['company'].id}) + + product = self.env['product.product'].create({ + 'name': 'Super Product', + 'seller_ids': [(0, 0, { + 'partner_id': self.partner_a.id, + 'min_qty': 1, + 'price': 10, + 'product_code': 'C01', + 'product_name': 'Name01', + 'sequence': 1, + }), (0, 0, { + 'partner_id': self.partner_a.id, + 'min_qty': 20, + 'price': 2, + 'product_code': 'C02', + 'product_name': 'Name02', + 'sequence': 2, + })] + }) + + orderpoint_form = Form(self.env['stock.warehouse.orderpoint']) + orderpoint_form.product_id = product + orderpoint_form.product_min_qty = 1 + orderpoint_form.product_max_qty = 0.000 + orderpoint_form.save() + + self.env['procurement.group'].run_scheduler() + + pol = self.env['purchase.order.line'].search([('product_id', '=', product.id)]) + self.assertEqual(pol.name, "[C01] Name01") + + with Form(pol.order_id) as po_form: + with po_form.order_line.edit(0) as pol_form: + pol_form.product_qty = 25 + self.assertEqual(pol.name, "[C02] Name02")