[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 <tic@odoo.com>
Signed-off-by: Adrien Widart <awt@odoo.com>
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user